PHP 5.6.40
Preview: main.js Size: 200.55 KB
/home/urbanman/roadkaraja.in/supportboard/js/main.js


/*
 * ==========================================================
 * MAIN SCRIPT
 * ==========================================================
 *
 * Main Javascript file. This file is shared by frond-end and back-end. © 2021 board.support. All rights reserved.
 * 
 */

'use strict';

(function ($) {

    var version = '3.1.9';
    var main;
    var global;
    var upload_target;
    var admin = false;
    var tickets = false;
    var timeout = false;
    var timeout_debounce = [];
    var previous_search;
    var sb_current_user = false;
    var chat;
    var chat_editor;
    var chat_textarea;
    var chat_header;
    var chat_status;
    var chat_emoji;
    var chat_scroll_area;
    var document_title = document.title;
    var CHAT_SETTINGS;
    var mobile = $(window).width() < 426;
    var today = new Date();
    var bot_id;
    var force_action = '';
    var dialogflow_human_takeover;
    var dialogflow_unknow_notify;
    var agents_online = false;
    var ND = 'undefined';
    var cookies_supported = true;
    var login_data = '';

    /*
    * ----------------------------------------------------------
    * EXTERNAL PLUGINS
    * ----------------------------------------------------------
    */

    // Auto Expand Scroll Area | Schiocco
    $.fn.extend({ manualExpandTextarea: function () { var t = this[0]; t.style.height = "auto", t.style.maxHeight = "25px"; window.getComputedStyle(t); t.style.height = (t.scrollHeight > 350 ? 350 : t.scrollHeight) + "px", t.style.maxHeight = "", $(t).trigger("textareaChanged") }, autoExpandTextarea: function () { var t = this[0]; t.addEventListener("input", function (e) { $(t).manualExpandTextarea() }, !1) } });

    // autolink-js
    (function () { var t = [].slice; String.prototype.autoLink = function () { var n, a, r, i, c, e, l; return e = /(^|[\s\n]|<[A-Za-z]*\/?>)((?:https?|ftp):\/\/[\-A-Z0-9+\u0026\u2019@#\/%?=()~_|!:,.;]*[\-A-Z0-9+\u0026@#\/%=~()_|])/gi, 0 < (c = 1 <= arguments.length ? t.call(arguments, 0) : []).length ? (i = c[0], n = i.callback, r = function () { var t; for (a in t = [], i) l = i[a], "callback" !== a && t.push(" " + a + "='" + l + "'"); return t }().join(""), this.replace(e, function (t, a, i) { return "" + a + (("function" == typeof n ? n(i) : void 0) || "<a href='" + i + "'" + r + ">" + i + "</a>") })) : this.replace(e, "$1<a href='$2'>$2</a>") } }).call(this);

    /*
    * ----------------------------------------------------------
    * # FUNCTIONS
    * ----------------------------------------------------------
    */

    var SBF = {

        // Main Ajax function
        ajax: function (data, onSuccess = false) {
            data['login-cookie'] = this.null(this.cookie('sb-login')) ? login_data : this.cookie('sb-login');
            if (!('user_id' in data) && activeUser()) {
                data['user_id'] = activeUser().id;
            }
            if (typeof SB_LANG != ND) {
                data['language'] = SB_LANG;
            }
            $.ajax({
                method: 'POST',
                url: SB_AJAX_URL,
                data: data
            }).done((response) => {
                let result;
                if (Array.isArray(response)) {
                    result = response;
                } else {
                    try {
                        result = JSON.parse(response);
                    } catch (e) {
                        console.log(response);
                        SBF.error('Json parse error', `SBF.ajax.${data['function']}`);
                    }
                }
                if (result[0] == 'success') {
                    if (onSuccess != false) onSuccess(result[1]);
                } else if (SBF.errorValidation(result)) {
                    if (onSuccess != false) onSuccess(result);
                } else {
                    if (result[1] == 'security-error') {
                        setTimeout(function () {
                            SBF.reset();
                        }, 1000);
                    }
                    SBF.error(result[1] + (SBF.null(result[2]) ? '' : '\nFunction name: ' + result[2]) + (SBF.null(result[3]) ? '' : '\nError message: ' + (typeof result[3] == 'string' ? result[3] : ('error' in result[3] && 'message' in result[3]['error'] ? `${result[3]['error']['message']} in function '${result[3]['error']['function']}'` : result[3]))), `SBF.ajax.${data['function']}`);
                }
            });
        },

        // Cors function
        cors: function (method = 'GET', url, onSuccess) {
            let xhr = new XMLHttpRequest();
            if ('withCredentials' in xhr) {
                xhr.open(method, url, true);
            } else if (typeof XDomainRequest != ND) {
                xhr = new XDomainRequest();
                xhr.open(method, url);
            } else {
                return false;
            }
            xhr.onload = function () {
                onSuccess(xhr.responseText);
            };
            xhr.onerror = function () {
                return false;
            };
            xhr.send();
        },

        // Uploads
        upload: function (form, onSuccess) {
            jQuery.ajax({
                url: SB_URL + '/include/upload.php',
                cache: false,
                contentType: false,
                processData: false,
                data: form,
                type: 'POST',
                success: function (response) {
                    onSuccess(response);
                }
            });
        },

        // Check if a variable is null or empty
        null: function (obj) { if (typeof (obj) !== 'undefined' && obj !== null && obj !== 'null' && obj !== false && (obj.length > 0 || typeof (obj) == 'number' || typeof (obj.length) == 'undefined') && obj !== 'undefined') return false; else return true; },

        // Deactivate and hide the elements
        deactivateAll: function () {
            $(global).find('.sb-popup, .sb-tooltip, .sb-list .sb-menu, .sb-select ul').sbActivate(false);
        },

        // Deselect the content of the target
        deselectAll: function () {
            if (window.getSelection) { window.getSelection().removeAllRanges(); }
            else if (document.selection) { document.selection.empty(); }
        },

        // Get URL parameters
        getURL: function (name = false, url = false) {
            if (url == false) {
                url = location.search;
            }
            if (name == false) {
                var c = url.split('?').pop().split('&');
                var p = {};
                for (var i = 0; i < c.length; i++) {
                    var d = c[i].split('=');
                    p[d[0]] = d[1]
                }
                return p;
            }
            if (url.indexOf('?') > 0) {
                url = url.substr(0, url.indexOf('?'));
            }
            return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(url) || [, ""])[1].replace(/\+/g, '%20') || "");
        },

        // Convert a json encoded string to normal text
        restoreJson: function (value) {
            if (SBF.null(value)) return '';
            return value.replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, "\t").replace(/\\f/g, "\f").replace(/\\'/g, "'").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
        },

        // Convert a string to slug and inverse
        stringToSlug: function (string) {
            string = string.trim();
            string = string.toLowerCase();
            var from = "åàáãäâèéëêìíïîòóöôùúüûñç·/_,:;";
            var to = "aaaaaaeeeeiiiioooouuuunc------";
            for (var i = 0, l = from.length; i < l; i++) {
                string = string.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i));
            }
            return string.replace(/[^a-z0-9 -]/g, '').replace(/\s+/g, '-').replace(/-+/g, '-').replace(/^-+/, '').replace(/-+$/, '').replace(/ /g, ' ');
        },

        slugToString: function (string) {
            string = string.replace(/_/g, ' ').replace(/-/g, ' ');
            return string.charAt(0).toUpperCase() + string.slice(1);
        },

        // Random string
        random: function () {
            let chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
            let result = '';
            for (var i = 5; i > 0; --i) result += chars[Math.floor(Math.random() * 62)];
            return result;
        },

        // Check if a user type is an agent
        isAgent: function (user_type) {
            return user_type == 'agent' || user_type == 'admin' || user_type == 'bot';
        },

        // Get cors elapsed of a given date from now
        beautifyTime: function (datetime, extended = false, future = false) {
            let date;
            if (datetime == '0000-00-00 00:00:00') return '';
            if (datetime.indexOf('-') > 0) {
                var arr = datetime.split(/[- :]/);
                date = new Date(arr[0], arr[1] - 1, arr[2], arr[3], arr[4], arr[5]);
            } else {
                var arr = datetime.split(/[. :]/);
                date = new Date(arr[2], arr[1] - 1, arr[0], arr[3], arr[4], arr[5]);
            }
            let date_string = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds()));
            let diff_days = Math.round(((new Date()) - date_string) / (1000 * 60 * 60 * 24)) * (future ? -1 : 1);
            let days = [sb_('Sunday'), sb_('Monday'), sb_('Tuesday'), sb_('Wednesday'), sb_('Thursday'), sb_('Friday'), sb_('Saturday')];
            let time = date_string.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
            if (diff_days < 1) {
                return (extended ? sb_('Today') + ' ' : '') + time;
            } else if (diff_days < 8) {
                return `<span>${days[date_string.getDay()]}</span>${extended ? ` <span>${time}</span>` : ''}`;
            } else {
                return `<span>${date_string.toLocaleDateString()}</span>${extended ? ` <span>${time}</span>` : ''}`;
            }
        },

        // Generate a string containing the agent location and time
        getLocationTimeString: function (details, onSuccess) {
            if ('timezone' in details) {
                let location = {};
                location['timezone'] = details['timezone']['value'];
                location['country'] = 'country' in details ? details['country']['value'] : location['timezone'].split('/')[0].replace(/_/g, ' ');
                location['city'] = 'city' in details ? details['city']['value'] : location['timezone'].split('/')[1].replace(/_/g, ' ');
                SBF.cors('GET', 'https://worldtimeapi.org/api/timezone/' + location['timezone'], function (response) {
                    response = JSON.parse(response);
                    if (SBF.null(response) || !SBF.null(response['error'])) {
                        SBF.error(response, 'SBF.getLocationTimeString()');
                        onSuccess(responseonSuccess);
                    } else {
                        let datetime = response['datetime'].replace('T', ' ');
                        onSuccess(`${new Date(datetime.substr(0, datetime.indexOf('.'))).toLocaleString([], { hour: '2-digit', minute: '2-digit' })} ${sb_('in')} ${location['city'] != '' ? location['city'] : ''}${location['country'] != '' ? ', ' + location['country'] : ''}`);
                    }
                });
            }
        },

        // Date string
        dateDB: function (date) {
            if (date == 'now') {
                date = (new Date).toISOString().replace('T', ' ');
                if (date.indexOf('.') > 0) {
                    date = date.substr(0, date.indexOf('.'));
                }
                return date;
            } else {
                return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`;
            }
        },

        // Set and get users last activity
        updateUsersActivity: function (user_id, return_user_id, onSuccess) {
            if (SBPusher.active) {
                onSuccess(SBPusher.online_ids.includes(return_user_id) ? 'online' : 'offline');
            } else {
                SBF.ajax({
                    function: 'update-users-last-activity',
                    user_id: user_id,
                    return_user_id: return_user_id,
                    check_slack: !admin && CHAT_SETTINGS['slack-active']
                }, (response) => {
                    if (response === 'online') {
                        onSuccess('online');
                    } else {
                        onSuccess('offline');
                    }
                });
            }
        },

        // Search functions
        search: function (search, searchFunction) {
            search = search.toLowerCase();
            if (search == previous_search) {
                $(global).find('.sb-search-btn i').sbLoading(false);
                return;
            }
            this.debounce(() => {
                previous_search = search;
                searchFunction();
            }, '#1');
        },

        searchClear: function (icon, onSuccess) {
            let search = $(icon).next().val();
            if (search != '') {
                $(icon).next().val('');
                onSuccess();
            }
        },

        // Support Board error js reporting
        error: function (message, function_name) {
            if (message instanceof Error) message = message.message;
            if (message[message.length - 1] == '.') message = message.slice(0, -1);
            if (message.includes('Support Board Error')) message = message.replace('Support Board Error ', '').replace(']:', ']');
            if (admin && message != '' && !function_name.indexOf('update-users-last-activity') && !function_name.startsWith('security-error')) SBF.dialog(`[Error] [${function_name}] ${message}. Check the console for more details.`, 'info');
            $(global).find('.sb-loading').sbLoading(false);
            SBF.event('SBError', { message: message, function_name: function_name });
            throw new Error(`Support Board Error [${function_name}]: ${message}.`);
        },

        errorValidation: function (response, code = true) {
            return Array.isArray(response) && response[0] === 'validation-error' && (code === true || response[1] == code);
        },

        // Login
        loginForm: function (button, area = false, onSuccess = false) {
            if (!$(button).sbLoading()) {
                if (area === false) area = $(button).closest('.sb-rich-login');
                let email = $(area).find('#email input').val();
                let password = $(area).find('#password input').val();
                if (email == '' || password == '') {
                    $(area).find('.sb-info').html(sb_('Please insert an email and password.')).sbActivate();
                } else {
                    SBF.ajax({
                        function: 'login',
                        email: email,
                        password: password
                    }, (response) => {
                        if (response != false && Array.isArray(response)) {
                            if (!admin && this.isAgent(response[0]['user_type'])) {
                                SBForm.showErrorMessage(area, 'You cannot sign in as an agent.');
                                SBChat.scrollBottom();
                            } else {
                                SBF.loginCookie(response[1]);
                                SBF.event('SBLoginForm', new SBUser(response[0]));
                                if (onSuccess !== false) onSuccess(response);
                            }
                        } else {
                            $(area).find('.sb-info').html(sb_('Invalid email or password.')).sbActivate();
                            if (!admin) SBChat.scrollBottom();
                        }
                        $(button).sbLoading(false);
                    });
                    $(area).find('.sb-info').html('').sbActivate(false);
                    $(button).sbLoading(true);
                }
            }
        },

        // Set the login cookie
        loginCookie: function (value) {
            login_data = value;
            this.cookie('sb-login', value, 3650, 'set');
        },

        // Login
        login: function (email = '', password = '', user_id = '', token = '', onSuccess = false) {
            SBF.ajax({
                function: 'login',
                email: email,
                password: password,
                user_id: user_id,
                token: token
            }, (response) => {
                if (response != false && Array.isArray(response)) {
                    this.loginCookie(response[1]);
                    if (onSuccess != false) {
                        onSuccess(response);
                    }
                    return true;
                } else {
                    return false;
                }
            });
        },

        // Logout
        logout: function (reload = true) {
            SBChat.stopRealTime();
            this.cookie('sb-login', '', '', 'delete');
            storage('open-conversation', '');
            SBChat.conversations = false;
            activeUser(false);
            if (typeof sb_beams_client !== ND) {
                sb_beams_client.stop();
            }
            SBF.ajax({
                function: 'logout'
            }, () => {
                SBF.event('SBLogout');
                if (reload) {
                    setTimeout(() => { location.reload() }, 500);
                }
            });
        },

        // Return the active user
        activeUser: function () {
            return activeUser();
        },

        // Get the active user
        getActiveUser: function (db = false, onSuccess) {
            let app_login = SBApps.login();
            if (!app_login && (storage('wp-login') || storage('whmcs-login') || storage('perfex-login') || storage('aecommerce-login'))) {
                this.cookie('sb-login', '', '', 'delete');
                activeUser(false);
                storage('wp-login', '');
                storage('whmcs-login', '');
                storage('perfex-login', '');
                storage('aecommerce-login', '');
            }
            SBF.ajax({
                function: 'get-active-user',
                db: db,
                login_app: app_login
            }, (response) => {
                if (!response) {
                    onSuccess();
                    return false;
                } else {
                    if ('cookie' in response) SBF.loginCookie(response['cookie']);
                    if ('user_type' in response) {
                        if (!admin && SBF.isAgent(response['user_type'])) {
                            let message = 'You are logged in as both agent and user. Logout or use another browser, Incognito or Private mode, to login as user. Force a logout by running the function SBF.reset() in the console.';
                            if (!storage('double-login-alert')) {
                                storage('double-login-alert', true);
                                alert(message);
                            }
                            console.warn('Support Board: ' + message);
                            SBF.event('SBDoubleLoginError');
                        } else {
                            activeUser(new SBUser(response));
                            if (app_login) {
                                storage(app_login[1] + '-login', true);
                            }
                            onSuccess();
                        }
                    }
                }
            });
        },

        // Clean
        reset: function () {
            this.cookie('sb-login', '', 0, false);
            try { localStorage.removeItem('support-board') } catch (e) { }
            this.logout();
        },

        // Dialog box
        dialog: function (text, type, onConfirm) {
            if (admin) {
                sbDialogAdmin(text, type, onConfirm);
            }
        },

        // Lightbox
        lightbox: function (content) {
            let lightbox = $(admin ? global : main).find('.sb-lightbox-media');
            $(lightbox).sbActivate().find(' > div').html(content);
        },

        // Manage the local storage
        storage: function (key, value = ND) {
            try { if (typeof localStorage == ND) return false } catch (e) { return false }
            let settings = localStorage.getItem('support-board');
            if (settings === null) {
                settings = {};
            } else {
                settings = JSON.parse(settings);
            }
            if (value === ND) {
                if (key in settings) {
                    return settings[key];
                } else {
                    return false;
                }
            } else {
                if (value == '') {
                    delete settings[key];
                } else {
                    settings[key] = value;
                }
                localStorage.setItem('support-board', JSON.stringify(settings));
            }
        },

        // Set or get a cookie
        cookie: function (name, value = false, expiration_days = false, action = 'get') {
            let cookie_https = location.protocol == 'https:' ? 'SameSite=None;Secure;' : '';
            let domain = CHAT_SETTINGS && CHAT_SETTINGS['cookie-domain'] ? 'domain=' + CHAT_SETTINGS['cookie-domain'] + ';' : '';
            if (action == 'get') {
                if (!cookies_supported) {
                    return this.storage(name);
                }
                let cookies = document.cookie.split(';');
                for (var i = 0; i < cookies.length; i++) {
                    var cookie = cookies[i];
                    while (cookie.charAt(0) == ' ') {
                        cookie = cookie.substring(1);
                    }
                    if (cookie.indexOf(name) == 0) {
                        let value = cookie.substring(name.length + 1, cookie.length);
                        return this.null(value) ? false : value;
                    }
                }
                return false;
            } else if (action == 'set') {
                if (!cookies_supported) {
                    this.storage(name, value);
                } else {
                    let date = new Date();
                    date.setTime(date.getTime() + (expiration_days * 24 * 60 * 60 * 1000));
                    document.cookie = name + "=" + value + ";expires=" + date.toUTCString() + ";path=/;" + cookie_https + domain;
                }
            } else if (this.cookie(name)) {
                if (!cookies_supported) {
                    this.storage(name, '');
                } else {
                    document.cookie = name + "=" + value + ";expires=Thu, 01 Jan 1970 00:00:01 GMT;path=/;" + cookie_https + domain;
                }
            }
        },

        // Save the current time or check if the saved time is older than now plus the given hours
        storageTime: function (key, hours = false) {
            if (hours === false) {
                storage(key, today.getTime());
            } else {
                return storage(key) == false || (today.getTime() - storage(key)) > (3600000 * hours);
            }
        },

        // Return a front setting
        setting: function (key) {
            return typeof CHAT_SETTINGS !== ND && key in CHAT_SETTINGS ? CHAT_SETTINGS[key] : -1;
        },

        // Return the shortcode array
        shortcode: function (shortcode) {
            return SBRichMessages.shortcode(shortcode);
        },

        // Events and webhooks
        event: function (name, parameters) {
            $(document).trigger(name, parameters);
            if (this.setting('webhooks') && ['SBLoginForm', 'SBUserDeleted', 'SBMessageSent', 'SBBotMessage', 'SBEmailSent', 'SBNewMessagesReceived', 'SBNewConversationReceived', 'SBNewConversationCreated', 'SBActiveConversationStatusUpdated', 'SBSlackMessageSent', 'SBMessageDeleted', 'SBRegistrationForm', 'SBRichMessageSubmit', 'SBNewEmailAddress'].includes(name)) {
                SBF.ajax({
                    function: 'webhooks',
                    function_name: name,
                    parameters: parameters
                });
            }
        },

        // Translate a string
        translate: function (string) {
            if (SBF.null(CHAT_SETTINGS)) return string;
            let translations = CHAT_SETTINGS['translations'];
            if (translations != false && string in translations) {
                return translations[string] == '' ? string : translations[string];
            } else {
                return string;
            }
        },

        // Escape a string
        escape: function (string) {
            string = replaceAll(string, '<script', '&lt;script');
            string = replaceAll(string, '</script', '&lt;/script');
            string = replaceAll(string, 'javascript:', '');
            string = replaceAll(string, 'onclick=', '');
            string = replaceAll(string, 'onerror=', '');
            return string;
        },

        // Visibility change function
        visibilityChange: function (visibility = '') {
            if (visibility == 'hidden') {
                if (!admin) {
                    SBChat.stopRealTime();
                }
                SBChat.tab_active = false;
            } else {
                if (activeUser() && !admin) {
                    SBChat.startRealTime();
                }
                SBChat.tab_active = true;
                document.title = document_title;
            }
        },

        // Convert a settings string to an Array
        settingsStringToArray: function (string) {
            if (this.null(string)) return [];
            let result = [];
            string = string.split(',');
            for (var i = 0; i < string.length; i++) {
                let values = string[i].split(':');
                result[values[0]] = values[1] == 'false' ? false : values[1] == 'true' ? true : values[1];
            }
            return result;
        },

        // Open a browser window
        openWindow: function (link, width = 550, height = 350) {
            let left = (screen.width / 2) - (width / 2);
            let top = (screen.height / 2) - (height / 2);
            window.open(link, 'targetWindow', 'toolbar=no,location=no,status=no,menubar=no,scrollbars=yes,resizable=yes,width=' + width + ',height=' + height + ', top=' + top + ', left=' + left);
            return false;
        },

        // Convert a date to local time
        convertUTCDateToLocalDate: function (date, UTCoffset = 0) {
            date = new Date(date); // Y/m/d H:i:s
            date = new Date(date.getTime() + UTCoffset * 3600000);
            return new Date(date.getTime() + (new Date()).getTimezoneOffset() * 60000 * -1);
        },

        // Load a js or css file
        loadResource: function (url, script = false) {
            let head = document.getElementsByTagName('head')[0];
            let resource = document.createElement(script ? 'script' : 'link');
            if (script) {
                resource.src = url + '?v=' + version;
            } else {
                resource.id = 'support-board';
                resource.rel = 'stylesheet';
                resource.type = 'text/css';
                resource.href = url + '?v=' + version;
                resource.media = 'all';
            }
            head.appendChild(resource);
        },

        // Debounce
        debounce: function (bounceFunction, id, interval = 500) {
            if (!(id in timeout_debounce)) {
                timeout_debounce[id] = true;
                bounceFunction();
                setTimeout(() => {
                    delete timeout_debounce[id];
                }, interval);
            }
        }
    }

    /*
    * ----------------------------------------------------------
    * # PUSHER
    * ----------------------------------------------------------
    */

    var SBPusher = {
        channels: {},
        channels_presence: [],
        active: false,
        pusher: false,
        pusher_beams: false,
        initialized: false,
        online_ids: [],

        // Initialize Pusher
        init: function (onSuccess = false) {
            if (SBPusher.active) {
                if (this.pusher) {
                    return onSuccess ? onSuccess() : true;
                } else if (onSuccess) {
                    $(window).one('SBPusherInit', () => {
                        onSuccess();
                    });
                } else return;
                this.initialized = true;
                $.getScript('https://js.pusher.com/7.0/pusher.min.js', () => {
                    this.pusher = new Pusher(admin ? SB_ADMIN_SETTINGS['pusher-key'] : CHAT_SETTINGS['pusher-key'], { cluster: admin ? SB_ADMIN_SETTINGS['pusher-cluster'] : CHAT_SETTINGS['pusher-cluster'], authEndpoint: SB_URL + '/include/pusher.php' });
                    SBF.event('SBPusherInit');
                });
            }
        },

        // Initialize Push notifications
        initPushNotifications: function () {
            if (activeUser() || admin) {
                $.getScript('https://js.pusher.com/beams/1.0/push-notifications-cdn.js', () => {
                    window.navigator.serviceWorker.ready.then((serviceWorkerRegistration) => {
                        this.pusher_beams = new PusherPushNotifications.Client({
                            instanceId: admin ? SB_ADMIN_SETTINGS['push-notifications-id'] : CHAT_SETTINGS['push-notifications-id'],
                            serviceWorkerRegistration: serviceWorkerRegistration,
                        });
                        this.pusher_beams.start().then(() => this.pusher_beams.setDeviceInterests(admin ? [SB_ACTIVE_AGENT['id'], 'agents'] : [activeUser().id, 'users'])).catch(console.error);

                    });
                });
            }
        },

        // Initialize service worker
        initServiceWorker: function () {
            if ('serviceWorker' in navigator) {
                navigator.serviceWorker.register(admin ? SB_URL + '/sw.js?v=' + version : CHAT_SETTINGS['push-notifications-url'] + '?v=' + version).then(function (registration) {
                    registration.update();
                }).catch(function (error) {
                    console.warn(error);
                });
                navigator.serviceWorker.onmessage = function (e) {
                    if (admin && e.data.conversation_id) {
                        SBAdmin.conversations.openConversation(e.data.conversation_id, e.data.user_id);
                    }
                    if (!admin && e.data == 'sb-open-chat') {
                        SBChat.open();
                    }
                };
            }
        },

        // Subscribe to a channel
        subscribe: function (channel_name, onSuccess = false) {
            if (!this.pusher) return this.init(() => { this.subscribe(channel_name, onSuccess) });
            let channel = this.pusher.subscribe(channel_name);
            channel.bind('pusher:subscription_error', (error) => {
                return console.log(error);
            });
            channel.bind('pusher:subscription_succeeded', () => {
                this.channels[channel_name] = channel;
                if (onSuccess) onSuccess();
            })
        },

        // Add event listener for a channel
        event: function (event, callback, channel = 'private-user-' + activeUser().id) {
            if (!this.pusher) return this.init(() => { this.event(event, callback, channel) });
            if (channel in this.channels) {
                this.channels[channel].unbind(event);
                this.channels[channel].bind(event, (data) => {
                    callback(data);
                });
            } else this.subscribe(channel, () => { this.event(event, callback, channel) });
        },

        // Trigger an event
        trigger: function (event, data = {}, channel = 'private-user-' + activeUser().id) {
            if (event.indexOf('client-') == 0) {
                return this.channels[channel].trigger(event, data);
            } else {
                SBF.ajax({
                    function: 'pusher-trigger',
                    channel: channel,
                    event: event,
                    data: data
                }, (response) => { return response });
            }
        },

        // Presence  
        presence: function (index = 1, onSuccess) {
            if (!this.pusher) return this.init(() => { this.presence() });
            let channel = this.pusher.subscribe('presence-' + index);
            channel.bind('pusher:subscription_succeeded', (members) => {
                if (members.count > 98) return this.subscribe(index + 1);
                members.each((member) => {
                    if (this.presenceCheck(member)) {
                        this.online_ids.push(member.id);
                    }
                });
                SBChat.updateUsersActivity();
                if (onSuccess) onSuccess();
            })
            channel.bind('pusher:subscription_error', (error) => {
                return console.log(error);
            });
            channel.bind('pusher:member_added', (member) => {
                if (this.presenceCheck(member)) {
                    this.presenceAdd(member.id);
                }
            });
            channel.bind('pusher:member_removed', (member) => {
                this.presenceRemove(member.id);
            });
            this.channels_presence.push(channel);
            if (!admin && CHAT_SETTINGS['slack-active']) {
                this.event('add-user-presence', (response) => {
                    this.presenceAdd(response.agent_id)
                });
                SBF.ajax({
                    function: 'slack-presence',
                    list: true
                }, (response) => {
                    for (var i = 0; i < response.length; i++) {
                        this.presenceAdd(response[i]);
                    }
                    SBChat.updateUsersActivity();
                });
            }
        },

        presenceCheck: function (member) {
            let agent = SBF.isAgent(member.info.user_type);
            return ((admin && !agent) || (!admin && agent)) && !this.online_ids.includes(member.id);
        },

        presenceAdd: function (user_id) {
            if (!this.online_ids.includes(user_id)) {
                this.online_ids.push(user_id);
                this.presenceUpdateAdmin();
                SBChat.updateUsersActivity();
            }
        },

        presenceRemove: function (user_id) {
            let index = this.online_ids.indexOf(user_id);
            if (index !== -1) {
                this.online_ids.splice(index, 1);
                this.presenceUpdateAdmin();
                SBChat.updateUsersActivity();
            }
        },

        presenceUnsubscribe: function () {
            for (var i = 0; i < this.channels_presence.length; i++) {
                this.channels_presence[i].unsubscribe('presence-' + (i + 1));
            }
        },

        presenceUpdateAdmin: function () {
            if (admin && $(global).find('.sb-area-users.sb-active [data-type="online"].sb-active').length) SBAdmin.users.update();
        },

        // Send a Push notification
        pushNotification: function (message) {
            let image = admin ? SB_ACTIVE_AGENT['profile_image'] : activeUser().image;
            SBF.ajax({
                function: 'push-notification',
                title: admin ? SB_ACTIVE_AGENT['full_name'] : activeUser().name,
                message: message,
                icon: image.indexOf('user.svg') > 0 ? CHAT_SETTINGS['notifications-icon'] : image,
                interests: admin ? activeUser().id : (SBChat.lastAgent(false) ? SBChat.lastAgent(false)['user_id'] : (SBF.null(SBChat.conversation.get('agent_id')) ? 'agents' : SBChat.conversation.get('agent_id'))),
                conversation_id: SBChat.conversation == false ? false : SBChat.conversation.id
            }, (response) => { return response });
        }
    }

    function replaceAll(string, search, replace) {
        if (SBF.null(string)) return string;
        var esc = search.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
        var reg = new RegExp(esc, 'ig');
        return string.replace(reg, replace);
    }

    function sbDelta(e) {
        let delta = e.originalEvent.wheelDelta;
        if (typeof delta == ND) {
            delta = e.originalEvent.deltaY;
        }
        if (typeof delta == ND) {
            delta = e.originalEvent.detail * -1;
        }
        return delta;
    }

    // Shortcut for local storage function
    function storage(key, value = ND) {
        return SBF.storage(key, value);
    }

    // Support Board js translations
    function sb_(string) {
        return SBF.translate(string);
    }

    // Access the global user variable
    function activeUser(value = -1) {
        if (value === -1) {
            return window.sb_current_user;
        } else {
            window.sb_current_user = value;
        }
    }

    /*
    * ----------------------------------------------------------
    * GLOBAL FUNCTIONS
    * ----------------------------------------------------------
    */

    window.SBF = SBF;
    window.SBPusher = SBPusher;
    window.sb_current_user = sb_current_user;

    /*
    * ----------------------------------------------------------
    * JQUERY FUNCTIONS
    * ----------------------------------------------------------
    */

    // Activate or deactivate the element
    $.fn.sbActivate = function (show = true) {
        $(this).setClass('sb-active', show);
        return this;
    };

    // Check if active 
    $.fn.sbActive = function () {
        return $(this).hasClass('sb-active');
    };

    // Loading check, set and unset
    $.fn.sbLoading = function (value = 'check') {
        if (value == 'check') {
            return $(this).hasClass('sb-loading');
        } else {
            $(this).setClass('sb-loading', value);
        }
        return this;
    }

    // Popup
    $.fn.sbTogglePopup = function (button) {
        var showed = true;
        if ($(this).sbActive()) {
            $(this).sbActivate(false);
            $(global).removeClass('sb-popup-active');
            showed = false;
        } else {
            $(global).addClass('sb-popup-active');
            $(global).find('.sb-popup').sbActivate(false);
            $(this).css('left', $(button).offset().left + 15).sbActivate();
            SBF.deselectAll();
        }
        return showed;
    };

    // Uploader
    $.fn.sbUploadFiles = function (onSuccess) {
        let files = $(this).prop('files');
        for (var i = 0; i < files.length; i++) {
            let file = files[i];
            let form = new FormData();
            form.append('file', file);
            SBF.upload(form, onSuccess);
        }
        $(this).value = '';
    }

    // Set profile box
    $.fn.setProfile = function (name = false, profile_image = false) {
        if (SBF.null(name)) name = activeUser() != false ? activeUser().name : '';
        if (SBF.null(profile_image)) profile_image = activeUser() != false ? activeUser().image : SB_URL + '/media/user.svg';
        $(this).find('img').attr('src', profile_image);
        $(this).find('.sb-name').html(name);
        return this;
    }

    // Add or remove a class
    $.fn.setClass = function (class_name, add = true) {
        if (add) {
            $(this).addClass(class_name);
        } else {
            $(this).removeClass(class_name);
        }
    }

    /* 
    * ----------------------------------------------------------
    * # USER
    * ----------------------------------------------------------
    */

    class SBUser {
        constructor(details = {}, extra = {}) {
            this.details = details;
            this.extra = extra;
            this.conversations = [];
            this.processArray(details);
        }

        get id() {
            return this.get('id') == '' ? this.get('user_id') : this.get('id');
        }

        get type() {
            return this.get('user_type');
        }

        get name() {
            return this.get('full_name');
        }

        get image() {
            return this.get('profile_image');
        }

        get language() {
            return this.getExtra('browser_language') == '' ? '' : this.getExtra('browser_language')['value'].toLowerCase();
        }

        get(id) {
            if (id == 'full_name') return 'first_name' in this.details ? this.details['first_name'] + (this.details['last_name'] != '' ? ' ' + this.details['last_name'] : '') : '';
            if (id in this.details && !SBF.null(this.details[id])) return this.details[id];
            else return '';
        }

        getExtra(id) {
            if (id in this.extra && !SBF.null(this.extra[id])) return this.extra[id];
            else return '';
        }

        set(id, value) {
            this.details[id] = value;
        }

        setExtra(id, value) {
            this.extra[id] = value;
        }

        // Initialization
        processArray(details) {
            if (details && 'details' in details) {
                for (var i = 0; i < details['details'].length; i++) {
                    this.setExtra(details['details'][i]['slug'], details['details'][i]);
                }
                delete details['details'];
                this.details = details;
            }
        }

        // Update the user details and extra details
        update(onSuccess) {
            if (this.id != '') {
                SBF.ajax({
                    function: 'get-user',
                    user_id: this.id,
                    extra: true
                }, (response) => {
                    this.processArray(response);
                    onSuccess();
                });
            } else {
                SBF.error('Missing user ID', 'SBUser.update');
            }
        }

        // Get user conversations
        getConversations(onSuccess = false, exclude_id) {
            if (this.id != '') {
                SBF.ajax({
                    function: 'get-user-conversations',
                    user_id: this.id,
                    exclude_id: exclude_id
                }, (response) => {
                    let conversations = [];
                    for (var i = 0; i < response.length; i++) {
                        let conversation = new SBConversation([new SBMessage(response[i])], { id: response[i]['conversation_id'], conversation_status_code: response[i]['conversation_status_code'], department: response[i]['department'], title: response[i]['title'] });
                        conversations.push(conversation);
                    }
                    this.conversations = conversations;
                    if (onSuccess != false) {
                        onSuccess(conversations);
                    }
                });
            } else {
                SBF.error('Missing user ID', 'SBUser.getConversations');
            }
        }

        // Get conversations code
        getConversationsCode(conversations = false) {
            let code = '';
            let active_conversation_id = SBChat.conversation != false ? SBChat.conversation.id : -1;
            if (conversations == false) conversations = this.conversations;
            for (var i = 0; i < conversations.length; i++) {
                if (conversations[i] instanceof SBConversation) {
                    code += `<li ${active_conversation_id == conversations[i].id ? 'class="sb-active" ' : ''}data-conversation-status="${conversations[i].get('conversation_status_code')}" data-conversation-id="${conversations[i].id}" data-department="${conversations[i].get('department')}">${conversations[i].getCode()}</li>`;
                } else {
                    SBF.error('Conversation not of type SBConversation', 'SBUser.getConversationsCode');
                }
            }
            if (code == '') {
                code = `<p class="sb-no-results">${sb_('No conversations found.')}</p>`;
            }
            return code;
        }

        // Get single conversation
        getFullConversation(conversation_id = false, onSuccess = false) {
            if (conversation_id !== false) {
                SBF.ajax({
                    function: 'get-conversation',
                    conversation_id: conversation_id
                }, (response) => {
                    let messages = [];
                    for (var i = 0; i < response['messages'].length; i++) {
                        messages.push(new SBMessage(response['messages'][i]));
                    }
                    if (onSuccess != false) {
                        onSuccess(new SBConversation(messages, response['details']));
                    }
                });
            } else {
                SBF.error('Missing conversation ID', 'SBUser.getFullConversation');
            }
        }

        getConversationByID(conversation_id) {
            for (var i = 0; i < this.conversations.length; i++) {
                if (this.conversations[i].id == conversation_id) {
                    return this.conversations[i];
                }
            }
            return false;
        }

        // Add a new conversation
        addConversation(conversation) {
            if (conversation instanceof SBConversation) {
                let conversation_id = conversation.id;
                let is_new = true;
                for (var i = 0; i < this.conversations.length; i++) {
                    if (this.conversations[i].id == conversation_id) {
                        this.conversations[i] = conversation;
                        is_new = false;
                        break;
                    }
                }
                if (is_new) {
                    this.conversations.unshift(conversation);
                }
                return is_new;
            } else {
                SBF.error('Conversation not of type SBConversation', 'SBUser.addConversation');
            }
        }

        // Get the last conversation
        getLastConversation() {
            return this.isConversationsEmpty() ? false : this.conversations[this.conversations.length - 1];
        }

        // Check if the conversation array is empty
        isConversationsEmpty() {
            return this.conversations.length == 0;
        }

        // Check if the extra array is empty
        isExtraEmpty() {
            return Object.keys(this.extra).length === 0 && this.extra.constructor === Object;
        }

        // Delete the user
        delete(onSuccess) {
            if (this.id != '') {
                SBF.ajax({
                    function: 'delete-user',
                    user_id: this.id
                }, () => {
                    SBF.event('SBUserDeleted', this.id);
                    onSuccess();
                    return true;
                });
            } else {
                SBF.error('Missing user ID', 'SBUser.delete');
            }
        }
    }
    window.SBUser = SBUser;

    /* 
    * ----------------------------------------------------------
    * # MESSAGE
    * ----------------------------------------------------------
    */

    class SBMessage {
        constructor(details) {
            this.details = details;
            if ('first_name' in this.details) {
                this.details['full_name'] = this.details['first_name'] + ' ' + this.details['last_name'];
            }
            this.set('payload', this.get('payload') != '' ? JSON.parse(this.get('payload')) : {});
        }

        get id() {
            return this.get('id');
        }

        get attachments() {
            return !SBF.null(this.details['attachments']) ? JSON.parse(this.details['attachments']) : [];
        }

        get(id) {
            if (id in this.details && !SBF.null(this.details[id])) return this.details[id];
            else return '';
        }

        set(id, value) {
            this.details[id] = value;
        }

        payload(key = false, value = false) {
            if (key !== false && value !== false) {
                let payload = this.get('payload');
                payload[key] = value;
                this.set('payload', payload);
            }
            return this.get('payload');
        }

        getCode() {
            let agent = SBF.isAgent(this.details['user_type']);
            let admin_menu = admin ? SBAdmin.conversations.messageMenu(agent) : '';
            let message = this.render(this.details['message']);
            let attachments = this.attachments;
            let attachments_code = '';
            let media_code = '';
            let thumb = admin || (agent && !CHAT_SETTINGS['hide-agents-thumb']) || (!agent && CHAT_SETTINGS['display-users-thumb']) ? `<div class="sb-thumb"><img src="${this.details['profile_image']}"><div>${this.details['full_name']}</div></div>` : '';
            let css = (!agent ? 'sb-right' : '') + (thumb == '' ? '' : ' sb-thumb-active');
            let type = '';
            if (message == '' && attachments.length == 0) {
                return '';
            }

            // Rich Messages
            if (agent) {
                let shortcodes = message.match(/\[.+?\]/g) || [];
                let rich = false;
                for (var i = 0; i < shortcodes.length; i++) {
                    let settings = SBRichMessages.shortcode(shortcodes[i]);
                    let rich_message = SBRichMessages.generate(settings[1], settings[0]);
                    if (rich_message != false) {
                        if (message.charAt(0) != '[') {
                            rich_message = rich_message.replace('sb-rich-message', 'sb-rich-message sb-margin-top');
                        }
                        if (message.charAt(message.length - 1) != ']') {
                            rich_message = rich_message.replace('sb-rich-message', 'sb-rich-message sb-margin-bottom');
                        }
                        message = message.replace(shortcodes[i], rich_message);
                        rich = true;
                        type = `data-type="${settings[0]}"`;
                    }
                }
                if (rich) {
                    css += ' sb-rich-cnt';
                    if (shortcodes.length > 1) {
                        type = 'data-type="multiple"';
                    }
                }
            }

            // Attachments
            if (attachments.length) {
                attachments_code = '<div class="sb-message-attachments">';
                for (var i = 0; i < attachments.length; i++) {
                    if (/.jpg|.jpeg|.png|.gif/.test(attachments[i][1])) {
                        media_code += `<div class="sb-image"><img src="${attachments[i][1]}" /></div>`;
                    } else {
                        attachments_code += `<a rel="noopener" target="_blank" href="${attachments[i][1]}">${attachments[i][0]}</a>`;
                    }
                }
                attachments_code += '</div>';
            }

            // Message creation
            return `<div data-id="${this.details['id']}" class="${css}" ${type}>${thumb}<div class="sb-cnt"><div class="sb-message${media_code != '' && message == '' ? ' sb-message-media' : ''}">${message}${media_code}</div>${attachments_code}<div class="sb-time">${SBF.beautifyTime(this.details['creation_time'], true)}</div></div>${admin_menu}</div>`;
        }

        render(message = false) {
            let len = message.length;
            if (message === false) {
                message = '' + this.details['message'];
            }

            // Code block
            let codes = message.match(/```([\s\S]*)```/) || [];
            for (var i = 0; i < codes.length; i++) {
                message = message.replace(codes[i], '[code-' + i + ']');
            }

            // Breakline
            message = message.replace(/(?:\r\n|\r|\n)/g, '<br>');

            // Bold
            message = message.replace(/\*([^\**]+)\*/g, "<b>$1</b>");

            // Italic
            message = message.replace(/\__([^\____]+)\__/g, "<i>$1</i>");

            // Strikethrough
            message = message.replace(/\~([^\~~]+)\~/g, "<del>$1</del>");

            // Code
            message = message.replace(/\`([^\``]+)\`/g, "<code>$1</code>");

            // Unicode
            message = message.replace('uccede', '[U1]');
            message = message.replace(/u([0-9a-f]{4,5})/mi, '&#x$1;');
            message = message.replace('[U1]', 'uccede');

            // Single emoji
            if (((len == 6 || len == 5) && message.startsWith('&#x')) || len < 3 && message.match(/(?:[\u2700-\u27bf]|(?:\ud83c[\udde6-\uddff]){2}|[\ud800-\udbff][\udc00-\udfff]|[\u0023-\u0039]\ufe0f?\u20e3|\u3299|\u3297|\u303d|\u3030|\u24c2|\ud83c[\udd70-\udd71]|\ud83c[\udd7e-\udd7f]|\ud83c\udd8e|\ud83c[\udd91-\udd9a]|\ud83c[\udde6-\uddff]|\ud83c[\ude01-\ude02]|\ud83c\ude1a|\ud83c\ude2f|\ud83c[\ude32-\ude3a]|\ud83c[\ude50-\ude51]|\u203c|\u2049|[\u25aa-\u25ab]|\u25b6|\u25c0|[\u25fb-\u25fe]|\u00a9|\u00ae|\u2122|\u2139|\ud83c\udc04|[\u2600-\u26FF]|\u2b05|\u2b06|\u2b07|\u2b1b|\u2b1c|\u2b50|\u2b55|\u231a|\u231b|\u2328|\u23cf|[\u23e9-\u23f3]|[\u23f8-\u23fa]|\ud83c\udccf|\u2934|\u2935|[\u2190-\u21ff])/)) {
                message = `<span class="emoji-large">${message}</span>`;
            }

            // Links
            if (message.includes('http')) {
                message = message.autoLink({ target: '_blank' });
            }

            // Code block restore
            for (var i = 0; i < codes.length; i++) {
                message = message.replace('[code-' + i + ']', '<pre>' + $.trim($.trim(codes[i]).substr(3, codes[i].length - 6).replace(/(?:\r\n|\r|\n)/g, '<br>'))) + '</pre>';
            }

            return message;
        }
    }
    window.SBMessage = SBMessage;

    /* 
    * ----------------------------------------------------------
    * # CONVERSATION
    * ----------------------------------------------------------
    */

    class SBConversation {
        constructor(messages, details) {
            this.details = SBF.null(details) ? {} : details;
            if (Array.isArray(messages)) {
                this.messages = [];
                if (messages.length) {
                    if (messages[0] instanceof SBMessage) {
                        this.messages = messages;
                    } else {
                        SBF.error('Messages not of type SBMessage', 'SBConversation.constructor');
                    }
                }
            } else {
                SBF.error('Message array not of type Array', 'SBConversation.constructor');
            }
        }

        get id() {
            return this.get('id') == '' ? this.get('conversation_id') : this.get('id');
        }

        get(id) {
            if (id in this.details && !SBF.null(this.details[id])) {
                return this.details[id];
            }
            if (id == 'title') {
                if (!SBF.null(this.details['first_name'])) {
                    return this.details['first_name'] + ' ' + this.details['last_name'];
                } else if (this.messages.length) {
                    return this.messages[0].get('full_name');
                }
            }
            return '';
        }

        set(id, value) {
            this.details[id] = value;
        }

        getMessage(id) {
            for (var i = 0; i < this.messages.length; i++) {
                if (this.messages[i].id == id) {
                    return this.messages[i];
                }
            }
            return false;
        }

        getLastMessage() {
            let count = this.messages.length;
            return count > 0 ? this.messages[count - 1] : false;
        }

        getLastUserMessage(index = false) {
            if (index === false) index = this.messages.length - 1;
            for (var i = index; i > -1; i--) {
                if (!SBF.isAgent(this.messages[i].get('user_type'))) {
                    return this.messages[i];
                }
            }
            return false;
        }

        updateMessage(id, message) {
            if (message instanceof SBMessage) {
                for (var i = 0; i < this.messages.length; i++) {
                    if (this.messages[i].id == id) {
                        this.messages[i] = message;
                        return true;
                    }
                }
            } else {
                SBF.error('Message not of type SBMessage', 'SBConversation.updateMessage');
            }
            return false;
        }

        addMessages(messages) {
            if (Array.isArray(messages)) {
                for (var i = 0; i < messages.length; i++) {
                    if (messages[i] instanceof SBMessage) {
                        this.messages.push(messages[i]);
                    }
                }
            } else {
                if (messages instanceof SBMessage) {
                    this.messages.push(messages);
                } else {
                    SBF.error('Messages not of type SBMessage', 'SBConversation.addMessages()');
                }
            }
        }

        getCode() {
            let count = this.messages.length;
            if (count) {
                let message = this.messages[count - 1];
                let text = message.get('message');
                if (text.indexOf('[') !== false) {
                    let shortcodes = text.match(/\[.+?\]/g) || [];
                    if (shortcodes.length) {
                        let shortcode = SBRichMessages.shortcode(shortcodes[0]);
                        text = text.replace(shortcodes[0], sb_(SBF.null(shortcode[1]['message']) ? (SBF.null(shortcode[1]['title']) ? '' : shortcode[1]['title']) : shortcode[1]['message']));
                    }
                }
                let title = tickets && !CHAT_SETTINGS['tickets-conversations-title-user'] ? this.get('title') : sb_(!admin && message.get('last_name').charAt(0) == '#' ? 'You' : message.get('full_name'));
                return `<div class="sb-conversation-item" data-user-id="${message.get('user_id')}"><img src="${message.get('profile_image')}"><div><span class="sb-name">${title}</span><span class="sb-time">${SBF.beautifyTime(message.get('creation_time'))}</span></div><div class="sb-message">${text.length > 114 ? text.substr(0, 114) + ' ...' : text}</div></div>`;
            }
            return '';
        }

        deleteMessage(id) {
            for (var i = 0; i < this.messages.length; i++) {
                if (this.messages[i].id == id) {
                    this.messages.splice(i, 1);
                    return true;
                }
            }
            return false;
        }

        searchMessages(search, exact_match = false) {
            let results = [];
            for (var i = 0; i < this.messages.length; i++) {
                let message = this.messages[i].get('message');
                if ((exact_match && message == search) || (!exact_match && message.includes(search))) {
                    results.push[messages[i]];
                }
            }
            return results;
        }

        getAttachments() {
            let list = [];
            for (var i = 0; i < this.messages.length; i++) {
                let attachments = this.messages[i].attachments;
                for (var j = 0; j < attachments.length; j++) {
                    let link = attachments[j][1];
                    list.push([attachments[j][0], link, link.substr(link.lastIndexOf('.') + 1), this.messages[i].id]);
                }
            }
            return list;
        }
    }
    window.SBConversation = SBConversation;

    /* 
    * ----------------------------------------------------------
    * # CHAT
    * ----------------------------------------------------------
    */

    var SBChat = {

        // Variables
        rich_messages_list: ['chips', 'buttons', 'select', 'inputs', 'table', 'list'],
        emoji_options: { range: 0, range_limit: 47, list: [], list_now: [], touch: false },
        initialized: false,
        editor_listening: false,
        conversation: false,
        is_busy: false,
        is_busy_update: false,
        chat_open: false,
        real_time: false,
        agent_id: -1,
        agent_online: false,
        user_online: false,
        expanded: false,
        main_header: true,
        start_header: false,
        desktop_notifications: false,
        flash_notifications: false,
        datetime_last_message: '2000-01-01 00:00:00',
        datetime_last_message_conversation: '2000-01-01 00:00:00',
        audio: false,
        audio_out: false,
        tab_active: true,
        notifications: [],
        typing_settings: { typing: false, sent: false, timeout: false },
        email_sent: false,
        dialogflow_token: storage('dialogflow-token') == false ? -1 : storage('dialogflow-token'),
        dashboard: false,
        articles: false,
        slack_channel: [-1, -1],
        skip: false,
        queue_interval: false,
        departments: false,
        default_department: null,
        default_agent: null,

        // Send a message
        sendMessage: function (user_id = -1, message = '', attachments = [], onSuccess = false, payload = false, conversation_status_code = false) {
            let bot_unknow_notify = (dialogflow_unknow_notify || dialogflow_human_takeover != false) && SBApps.dialogflow.active();
            let bot_status_code = bot_unknow_notify && SBApps.dialogflow.active() ? 0 : 2;
            let last_message = this.conversation ? this.conversation.getLastMessage() : false;

            // Check settings and contents
            conversation_status_code = conversation_status_code === false ? (last_message ? (admin ? 1 : bot_status_code) : (admin ? 1 : -1)) : conversation_status_code;
            if (this.conversation == false) {
                let last_conversation = admin ? false : activeUser().getLastConversation();
                if (last_conversation && force_action != 'new-conversation') {
                    this.openConversation(last_conversation.id);
                    this.setConversation(last_conversation);
                    force_action = false;
                } else {
                    this.newConversation(conversation_status_code, user_id, '', [], (admin && SB_ACTIVE_AGENT['department'] != '' ? SB_ACTIVE_AGENT['department'] : null), null, () => {
                        return this.sendMessage(user_id, message, attachments, onSuccess, payload);
                    });
                    return false;
                }
            }
            if (user_id == -1) {
                user_id = admin ? SB_ACTIVE_AGENT['id'] : activeUser().id;
            }
            if (message == '') {
                message = $(chat_textarea).val().trim();
            }
            if (attachments.length == 0) {
                $(chat_editor).find('.sb-attachments > div').each(function () {
                    attachments.push([$(this).attr('data-name'), $(this).attr('data-value')]);
                });
            }

            // Send message
            if (message != '' || attachments.length || payload) {
                let is_user = user_id != bot_id;
                this.busy(true);
                $(chat_textarea).val('').css('height', '');
                $(chat_editor).find('.sb-attachments').html('');
                this.activateBar(false);

                SBF.ajax({
                    function: 'send-message',
                    user_id: user_id,
                    conversation_id: this.conversation.id,
                    message: message,
                    attachments: attachments,
                    conversation_status_code: conversation_status_code,
                    queue: !admin && CHAT_SETTINGS['queue'] && is_user,
                    payload: payload
                }, (response) => {
                    let is_conversation = this.conversation != false;
                    let send_slack = admin || !bot_unknow_notify

                    // Update the chat current conversation
                    if ((admin && !this.user_online) || (!admin && !this.agent_online)) {
                        this.update();
                    }

                    // Update the dashboard conversations
                    if (!admin && this.dashboard && user_id == bot_id) {
                        this.updateConversations();
                    }

                    // Follow up
                    if (!admin && is_user && CHAT_SETTINGS['follow']['active']) {
                        this.followUp();
                    }

                    // Email notifications
                    if (is_user && (!bot_unknow_notify || !SBF.null(payload['skip-dialogflow'])) && ((!admin && CHAT_SETTINGS['notify-agent-email']) || (admin && !this.user_online && CHAT_SETTINGS['notify-user-email'] && activeUser().get('email') != ''))) {
                        this.sendEmail(message, attachments);
                    }

                    // Push notifications
                    if (CHAT_SETTINGS['push-notifications'] && ((!admin && is_user && !bot_unknow_notify) || admin)) {
                        SBPusher.pushNotification(message);
                    }

                    // Dialogflow
                    if (is_user && (!payload || (payload['id'] != 'sb-human-takeover' && SBF.null(payload['skip-dialogflow'])))) {
                        this.sendBotMessage(message, attachments);
                    }

                    // Slack and visitor to lead
                    if (!admin && is_user && activeUser().type == 'visitor') {
                        SBF.ajax({ function: 'update-user-to-lead', user_id: user_id }, () => {
                            activeUser().set('user_type', 'lead');
                            if (CHAT_SETTINGS['slack-active'] && send_slack) {
                                this.slackMessage(user_id, activeUser().name, activeUser().image, message, attachments);
                            }
                        });
                    } else if (CHAT_SETTINGS['slack-active'] && send_slack && !this.skip) {
                        if (admin) {
                            this.slackMessage(activeUser().id, SB_ACTIVE_AGENT['full_name'], SB_ACTIVE_AGENT['profile_image'], message, attachments);
                        } else {
                            this.slackMessage(activeUser().id, (is_user ? activeUser().name : CHAT_SETTINGS['bot-name']), (is_user ? activeUser().image : CHAT_SETTINGS['bot-image']), message, attachments);
                        }
                    }

                    // Timetable
                    if (is_user && !admin && CHAT_SETTINGS['timetable'] && (!CHAT_SETTINGS['office-hours'] || !agents_online) && SBF.storageTime('timetable', 1)) {
                        setTimeout(() => {
                            if (is_conversation) {
                                this.sendMessage(bot_id, CHAT_SETTINGS['timetable-hide'] ? CHAT_SETTINGS['timetable-message'] : '[timetable]');
                                this.scrollBottom();
                                SBF.storageTime('timetable');
                            }
                        }, 5000);
                    }

                    // Articles
                    if (this.articles && !admin && CHAT_SETTINGS['articles'] && !CHAT_SETTINGS['office-hours'] && !this.isInitDashboard()) {
                        setTimeout(() => {
                            if (is_conversation) {
                                this.sendMessage(bot_id, '[articles]');
                                this.scrollBottom();
                                this.articles = false;
                            }
                        }, 5000);
                    }

                    // Queue
                    if (response['queue']) {
                        this.queue(this.conversation.id);
                    }

                    // Event
                    let message_response = { user_id: user_id, conversation_id: this.conversation.id, conversation_status_code: conversation_status_code, message_id: response['message-id'], message: message, attachments: attachments };
                    SBF.event('SBMessageSent', message_response);
                    if (onSuccess !== false) {
                        onSuccess(message_response);
                    }

                    // Miscellaneous
                    if (this.skip) this.skip = false;
                    this.busy(false);
                });

                // Display the message as sending in progress
                if (is_user) {
                    message = SBF.escape(message);
                    $(chat).append((new SBMessage({ id: 'sending', profile_image: (admin ? SB_ACTIVE_AGENT['profile_image'] : activeUser().image), full_name: activeUser().name, creation_time: '0000-00-00 00:00:00', message: message, user_type: (admin ? 'agent' : 'user') })).getCode().replace('<div class="sb-time"></div>', `<div class="sb-time">${sb_('Sending')}<i></i></div>`));
                    if (!this.dashboard) this.scrollBottom();
                }

                // Sounds
                if ((!admin && this.chat_open && is_user && ['a', 'aa'].includes(CHAT_SETTINGS['chat-sound'])) || (admin && SB_ADMIN_SETTINGS['sounds'] == 'a')) {
                    this.audio_out.play();
                }
            }
        },

        // Dialogflow bot message
        sendBotMessage: function (message = '', attachments = []) {
            if (SBApps.dialogflow.active()) {
                timeout = setTimeout(() => {
                    this.typing(-1, 'start');
                    setTimeout(() => {
                        this.typing(-1, 'stop');
                    }, 10000);
                }, 1000);
                setTimeout(() => {
                    SBF.ajax({
                        function: 'send-bot-message',
                        conversation_id: this.conversation.id,
                        message: message,
                        attachments: attachments,
                        token: this.dialogflow_token
                    }, (response) => {
                        if (!SBF.errorValidation(response)) {
                            let is_unknow = !SBF.null(response['response']['queryResult']) && response['response']['queryResult']['action'] == 'input.unknown';
                            let send_notification = false;
                            let messages = 'messages' in response && Array.isArray(response['messages']) ? response['messages'] : [];
                            this.typing(-1, 'stop');
                            clearTimeout(timeout);
                            if (this.dialogflow_token != response['token']) {
                                this.dialogflow_token = response['token'];
                                storage('dialogflow-token', response['token']);
                            }
                            if (is_unknow && (dialogflow_unknow_notify || CHAT_SETTINGS['dialogflow-disable'])) {
                                SBApps.dialogflow.active(false);
                            }
                            if (is_unknow && dialogflow_human_takeover != false && SBF.storageTime('human-takeover', 1)) {
                                setTimeout(() => {
                                    this.sendMessage(bot_id, `[chips id="sb-human-takeover" options="${dialogflow_human_takeover['confirm']},${dialogflow_human_takeover['cancel']}" message="${dialogflow_human_takeover['message'].replace(/"/g, '')}"]`);
                                }, 1100);
                            }
                            if ('queryResult' in response['response']) {
                                let result = response['response']['queryResult'];

                                // Actions
                                if ('action' in result) {
                                    let action = result['action'];
                                    if (action == 'end') {
                                        SBApps.dialogflow.active(false);
                                    }
                                    SBF.event('SBBotAction', action);
                                }

                                // Payload
                                for (var i = 0; i < messages.length; i++) {
                                    if ('payload' in messages[i]) {
                                        let payloads = ['human-takeover', 'redirect', 'woocommerce-update-cart', 'woocommerce-checkout', 'open-article'];
                                        let payload = messages[i]['payload'];
                                        if (SBF.null(payload)) payload = [];
                                        if (payloads[0] in payload && payload[payloads[0]] === true) {
                                            send_notification = true;
                                            if (CHAT_SETTINGS['dialogflow-disable']) SBApps.dialogflow.active(false);
                                        }
                                        if (payloads[1] in payload) {
                                            setTimeout(function () {
                                                'new-window' in payload && payload['new-window'] ? window.open(payload[payloads[1]]) : document.location = payload[payloads[1]];
                                            }, 500);
                                        }
                                        if (payloads[2] in payload) {
                                            let payload_value = payload[payloads[2]];
                                            SBApps.woocommerce.updateCart(payload_value[0], payload_value[1], (response) => {
                                                if (response) {
                                                    SBF.event('SBWoocommerceCartUpdated', { 'action': payload_value[0], 'product': payload_value[1] });
                                                }
                                            });
                                        }
                                        if (payloads[3] in payload && payload[payloads[3]] === true) {
                                            SBApps.wordpress.ajax('url', { url_name: 'checkout' }, (response) => {
                                                setTimeout(function () {
                                                    document.location = response;
                                                }, 500);
                                            });
                                        }
                                        if (payloads[4] in payload) {
                                            SBChat.showArticles(payload[payloads[4]]);
                                        }
                                        SBF.event('SBBotPayload', payload);
                                    }
                                }

                                // Diagnostic Info
                                if ('diagnosticInfo' in result) {
                                    let info = result['diagnosticInfo'];

                                    // End conversation
                                    if ('end_conversation' in info && info['end_conversation']) {
                                        SBApps.dialogflow.active(false);
                                    }
                                }
                            }

                            // Slack
                            if (CHAT_SETTINGS['slack-active'] && messages && (!dialogflow_unknow_notify || is_unknow || send_notification)) {
                                if (is_unknow || send_notification) {
                                    let last_user_message = this.conversation.getLastUserMessage();
                                    this.slackMessage(activeUser().id, activeUser().name, activeUser().image, last_user_message.get('message'));
                                }
                                for (var i = 0; i < messages.length; i++) {
                                    this.slackMessage(activeUser().id, CHAT_SETTINGS['bot-name'], CHAT_SETTINGS['bot-image'], messages[i]['message'], messages[i]['attachments']);
                                }
                            }

                            // Email notification to agents if unknow answer
                            if (send_notification || (!admin && CHAT_SETTINGS['notify-agent-email'] && dialogflow_unknow_notify && is_unknow)) {
                                SBApps.dialogflow.unknowEmail(message);
                            }

                            // Push notification to agents if unknow answer
                            if (send_notification || (!admin && CHAT_SETTINGS['push-notifications'] && dialogflow_unknow_notify && is_unknow)) {
                                SBPusher.pushNotification(message);
                            }

                            SBF.event('SBBotMessage', { response: response, message: message });
                        }
                    });
                }, CHAT_SETTINGS['bot-delay'] == 0 ? 2000 : parseInt(CHAT_SETTINGS['bot-delay']));
            }
        },

        // Update message
        updateMessage: function (message_id, message = '') {
            SBF.ajax({
                function: 'update-message',
                message_id: message_id,
                message: message
            });
        },

        // Email notifications
        sendEmail: function (message, attachments) {
            let recipent_id = admin ? activeUser().id : (this.lastAgent(false) ? this.lastAgent(false)['user_id'] : -1);
            if (!admin && recipent_id != -1 && this.agent_online) {
                return false;
            }
            SBF.ajax({
                function: 'create-email',
                recipient_id: recipent_id,
                sender_name: admin ? SB_ACTIVE_AGENT['full_name'] : activeUser().name,
                sender_profile_image: admin ? SB_ACTIVE_AGENT['profile_image'] : activeUser().image,
                message: message,
                attachments: attachments,
                conversation_id: SBChat.conversation ? SBChat.conversation.id : false
            }, () => {
                this.email_sent = true;
                SBF.event('SBEmailSent', { recipient_id: recipent_id, message: message, attachments: attachments });
            });
        },

        // Push notifications. [Deprecated. This function will be removed soon.]
        pushNotification: function (message) {
            SBPusher.pushNotification(message);
        },

        // Desktop notifications
        desktopNotification: function (title, message, icon, conversation_id = false, user_id = false) {
            if (Notification.permission !== 'granted') {
                Notification.requestPermission();
            } else {
                let notify = new Notification(title, {
                    body: message,
                    icon: icon.indexOf('user.svg') > 0 ? CHAT_SETTINGS['notifications-icon'] : icon
                });
                notify.onclick = () => {
                    if (admin) {
                        if (conversation_id) {
                            SBAdmin.conversations.openConversation(conversation_id, user_id == false ? activeUser().id : user_id);
                        }
                    } else {
                        this.start();
                    }
                    window.focus();
                }
            }
        },

        // Editor submit message
        submit: function () {
            if (!this.is_busy) {
                if (activeUser() == false && !admin) {
                    this.addUserAndLogin(() => {
                        this.newConversation(2, -1, '', [], null, null, () => {
                            this.sendMessage();
                        });
                    });
                    this.busy(true);
                    return;
                } else {
                    this.sendMessage();
                }
            }
        },

        // Initialize the chat
        initChat: function () {
            SBF.getActiveUser(true, () => {
                let active = activeUser() !== false;
                let user_type = active ? activeUser().type : false;
                if (!admin && !tickets && CHAT_SETTINGS['popup'] && !storage('popup') && (!mobile || !CHAT_SETTINGS['popup-mobile-hidden'])) {
                    this.popup();
                }
                if (!admin && !tickets && CHAT_SETTINGS['privacy'] && !CHAT_SETTINGS['registration-required'] && !storage('privacy-approved')) {
                    this.privacy();
                    return;
                }
                if (typeof Notification !== ND && !CHAT_SETTINGS['push-notifications-users'] && (CHAT_SETTINGS['desktop-notifications'] == 'all' || (!admin && CHAT_SETTINGS['desktop-notifications'] == 'users') || (admin && CHAT_SETTINGS['desktop-notifications'] == 'agents'))) {
                    this.desktop_notifications = true;
                }
                if (CHAT_SETTINGS['flash-notifications'] == 'all' || (!admin && CHAT_SETTINGS['flash-notifications'] == 'users') || (admin && CHAT_SETTINGS['flash-notifications'] == 'agents')) {
                    this.flash_notifications = true;
                }
                if (this.registration(true) && !tickets) {
                    if (typeof gst_user_email !== ND) {
                        SBF.ajax({
                            function: 'auto-login',
                            user_email: gst_user_email,
                            user_name: gst_name_chat,
                        }, (response) => {
                            SBF.loginCookie(response[1]);
                            activeUser(new SBUser(response[0]));
//                            if (onSuccess !== false) {
//                                onSuccess(response);
//                            }
                            this.populateConversations();
                            setTimeout(function () {
                                SBChat.openConversation($('.sb-user-conversations').find('li').attr('data-conversation-id')); 
                            }, 500);          
                        });

                    } else {
                        this.registration();
                    }
                    return;
                }
                if (!active && (typeof SB_WP_WAITING_LIST !== ND || CHAT_SETTINGS['visitors-registration'] || CHAT_SETTINGS['welcome'] || CHAT_SETTINGS['subscribe'] || tickets) && (!tickets || !CHAT_SETTINGS['tickets-registration-required'])) {
                    this.addUserAndLogin(() => {
                        this.welcome();
                        this.subscribe();
                        SBApps.woocommerce.waitingList();
                        this.finalizeInit();
                    });
                } else if (this.conversation == false && active) {
                    this.populateConversations();
                } else {
                    this.finalizeInit();
                }
                if (CHAT_SETTINGS['header-name'] && active && user_type == 'user' && !tickets) {
                    $(chat_header).find('.sb-title').html(`${sb_('Hello')} ${activeUser().name}!`);
                }
                this.welcome();
                this.subscribe();
                SBApps.woocommerce.waitingList();
                if (!SBPusher.active) {
                    setInterval(() => {
                        this.updateConversations();
                        this.updateUsersActivity();
                    }, 10000);
                }
                this.scrollBottom(true);
            });
        },

        finalizeInit: function () {
            if (!this.initialized) {
                $(main).attr('style', '');
                if (!admin && !tickets) {
                    if (this.isInitDashboard()) {
                        this.showDashboard();
                    }
                    if (!mobile && window.innerHeight < 760) {
                        $(main).find(' > .sb-body').css('max-height', (window.innerHeight - 130) + 'px');
                    }
                }
                this.initialized = true;
                if (!admin) {
                    if (storage('open-conversation') && activeUser() && !this.registration(true)) {
                        this.openConversation(storage('open-conversation'));
                    }
                    if (!this.chat_open && storage('chat-open')) {
                        setTimeout(() => { this.start(); }, 500);
                    }
                    if (CHAT_SETTINGS['woocommerce-returning-visitor']) {
                        if (storage('returning-visitor') === false) {
                            SBF.storageTime('returning-visitor');
                        } else if (SBF.storageTime('returning-visitor', 24) && !storage('returning-visitor-processed')) {
                            setTimeout(() => {
                                SBF.ajax({
                                    function: 'woocommerce-returning-visitor'
                                }, () => {
                                    storage('returning-visitor-processed', true);
                                });
                            }, 15000);
                        }
                    }
                    if (CHAT_SETTINGS['push-notifications'] && activeUser()) {
                        SBPusher.initPushNotifications();
                    }
                    if (SBPusher.active) {
                        SBPusher.init(() => {
                            SBPusher.event('client-typing', (response) => {
                                if (response.user_id == this.agent_id && this.conversation && response.conversation_id == this.conversation.id) {
                                    this.typing(-1, 'start');
                                    clearTimeout(timeout);
                                    timeout = setTimeout(() => { this.typing(-1, 'stop'); }, 1000);
                                }
                            });
                            SBPusher.event('new-message', () => {
                                this.update();
                            });
                            SBPusher.event('update-conversations', () => {
                                this.updateConversations();
                            }, 'global');
                            SBPusher.presence();
                        });
                    }
                }
                SBF.event('SBInit');
            }
        },

        // Initialize the chat settings and open the chat
        start: function () {
            if (this.initialized) {
                this.populate();
                this.headerAgent();
                this.updateUsersActivity();
                this.startRealTime();
                this.chat_open = true;
                this.popup(true);
                if (this.conversation != false) {
                    this.updateNotifications('remove', this.conversation.id);
                }
                $(main).sbActivate();
                $('body').addClass('sb-chat-open');
                if (CHAT_SETTINGS['welcome-trigger'] == 'open') {
                    this.welcome();
                }
            }
        },

        // Open or close the chat
        open: function (open = true) {
            if (open && !this.chat_open) {
                this.start();
                this.chat_open = true;
                this.startRealTime();
                $(main).sbActivate();
                $('body').addClass('sb-chat-open');
                storage('chat-open', true);
                SBF.event('SBChatOpen');
            } else if (!open && this.chat_open) {
                $(main).sbActivate(false);
                this.stopRealTime();
                this.chat_open = false;
                storage('chat-open', false);
                $('body').removeClass('sb-chat-open');
                SBF.event('SBChatClose');
            }
        },

        // Get a full conversation and display it in the chat
        openConversation: function (conversation_id) {
            activeUser().getFullConversation(conversation_id, (response) => {
                if (response['id'] == '') {
                    storage('open-conversation', '');
                    return false;
                }
                this.setConversation(response);
                this.hideDashboard();
                this.populate();
                this.main_header = false;
                if (storage('queue') == conversation_id) {
                    this.queue(conversation_id);
                }
                if ((this.chat_open || tickets) && response.get('conversation_status_code') != 0) {
                    this.updateNotifications('remove', conversation_id);
                }
                storage('open-conversation', conversation_id);
                SBF.event('SBConversationOpen', response);
            });
        },

        // Update the active conversation with the latest messages
        update: function () {
            if (this.conversation != false && !this.is_busy_update) {
                let last_message = this.conversation.getLastMessage();
                let is_update = false;
                SBF.ajax({
                    function: 'get-new-messages',
                    conversation_id: this.conversation.id,
                    datetime: this.datetime_last_message_conversation
                }, (response) => {
                    let count = response.length;
                    this.is_busy_update = false;
                    if (this.conversation != false) {
                        if (Array.isArray(response) && count > 0 && (!last_message || last_message.id != response[count - 1]['id'] || last_message.get('message') != response[count - 1]['message'])) {
                            let code = '';
                            let messages = [];
                            let id_check = [];

                            // Generate and add the new messages
                            for (var i = 0; i < count; i++) {
                                if (!id_check.includes(response[i]['id'])) {
                                    let message = new SBMessage(response[i]);
                                    let payload = message.payload();
                                    this.datetime_last_message_conversation = message.get('creation_time');

                                    // Payload
                                    if (typeof payload !== 'boolean' && 'event' in payload) {
                                        let event = payload['event'];
                                        if ((event == 'delete-message' && this.conversation.getMessage(message.id) !== false) || (!admin && message.get('message') == '' && !message.attachments.length)) {
                                            this.deleteMessage(message.id);
                                        }
                                        if (event == 'chips-click' && payload['id'] == 'sb-human-takeover' && dialogflow_human_takeover['confirm'] == payload['result']) {
                                            if (CHAT_SETTINGS['dialogflow-disable']) SBApps.dialogflow.active(false);
                                            SBApps.dialogflow.unknowEmail(this.conversation.getLastUserMessage().get('message'));
                                            this.setConversationStatus(2);
                                            SBF.storageTime('human-takeover');
                                            setTimeout(() => {
                                                if (dialogflow_human_takeover['email'] && SBF.null(activeUser().get('email'))) {
                                                    this.sendMessage(bot_id, `[email id="sb-human-takeover-email" message="${dialogflow_human_takeover['email-message']}" placeholder="${CHAT_SETTINGS['follow']['placeholder']}" name="${CHAT_SETTINGS['follow']['name']}" last-name="${CHAT_SETTINGS['follow']['last-name']}" success="${dialogflow_human_takeover['success']}"]`);
                                                } else if (dialogflow_human_takeover['message-confirmation'] != '') {
                                                    this.sendMessage(bot_id, dialogflow_human_takeover['success']);
                                                }
                                            }, 1100);
                                        }
                                        if (event == 'woocommerce-update-cart' && !admin) {
                                            SBApps.woocommerce.updateCart(payload['action'], payload['id']);
                                        }
                                    }

                                    // Message
                                    if (this.conversation.getMessage(response[i]['id']) == false) {
                                        this.conversation.addMessages(message);
                                        code += message.getCode();
                                    } else {
                                        this.conversation.updateMessage(message.id, message);
                                        $(chat).find(`[data-id="${message.id}"]`).replaceWith(message.getCode());
                                        is_update = true;
                                    }
                                    messages.push(message);
                                    id_check.push(message.id);
                                }
                            }
                            $(chat).append(code);

                            // Update status code
                            let last_message = this.conversation.getLastMessage();
                            let user_type = last_message.get('user_type');
                            let is_agent = SBF.isAgent(user_type);
                            if (!admin && this.chat_open && is_agent && user_type != 'bot') {
                                if (last_message.get('message').indexOf('sb-rich-success') == -1) {
                                    this.setConversationStatus(0);
                                }
                                if (CHAT_SETTINGS['follow']['active']) {
                                    clearTimeout(timeout);
                                }
                                SBApps.dialogflow.active(false);
                            }

                            // Queue
                            if (storage('queue') == this.conversation.id && is_agent && user_type != 'bot') {
                                this.queue('clear');
                            }

                            // Miscellaneous
                            $(chat).find('[data-id="sending"]').remove();
                            this.headerAgent();
                            if (!this.dashboard && !is_update) {
                                this.scrollBottom();
                                setTimeout(() => {
                                    this.scrollBottom();
                                }, 300);
                            }
                            this.typing(-1, 'stop');
                            if ((!admin && this.chat_open && (is_agent || user_type == 'bot') && ['aa', 'ia'].includes(CHAT_SETTINGS['chat-sound'])) || (admin && !SBF.isAgent(user_type) && ['a', 'i', 'ic'].includes(SB_ADMIN_SETTINGS['sounds']))) {
                                this.audio.play();
                            }
                            SBF.event('SBNewMessagesReceived', messages);
                        }
                    }
                });
                this.is_busy_update = true;
            }
        },

        // Update the user conversations list with the latest conversations and messages
        updateConversations: function () {
            if (activeUser() != false) {
                SBF.ajax({
                    function: 'get-new-user-conversations',
                    datetime: this.datetime_last_message
                }, (response) => {
                    if (response.length) {
                        this.datetime_last_message = response[0]['creation_time'];
                        for (var i = 0; i < response.length; i++) {
                            let conversation_id = response[i]['conversation_id'];
                            let message = new SBMessage(response[i]);
                            let status_code = response[i]['conversation_status_code'];
                            let conversation = new SBConversation([message], { id: conversation_id, conversation_status_code: status_code, department: response[i]['department'], title: response[i]['title'] });

                            // Add or update the conversation
                            let is_new = activeUser().addConversation(conversation);
                            if (this.conversation == false) {
                                this.conversation = conversation;
                            }

                            // Red notifications
                            if (response[i]['user_id'] != activeUser().id && (this.conversation.id != conversation_id || !this.chat_open)) {
                                this.updateNotifications('add', conversation_id);
                                if (CHAT_SETTINGS['auto-open']) {
                                    this.start();
                                }
                            }

                            // Payload
                            let payload = message.payload();
                            if (typeof payload !== 'boolean' && 'event' in payload) {
                                let event = payload['event'];
                                if (event == 'open-chat') {
                                    if (this.conversation.id != conversation_id || this.dashboard) {
                                        this.openConversation(conversation_id);
                                    }
                                    setTimeout(() => { this.open() }, 500);
                                }
                                if (message.get('message') == '' && !message.attachments.length) {
                                    continue;
                                }
                            }

                            if (!this.tab_active) {

                                // Desktop notifications
                                if (this.desktop_notifications) {
                                    SBChat.desktopNotification(message.get('full_name'), message.get('message'), message.get('profile_image'));
                                }

                                // Flash notificaitons
                                if (this.flash_notifications) {
                                    document.title = sb_('New message...');
                                }
                            }
                            if (is_new) {
                                SBF.event('SBNewConversationReceived', conversation);
                            }
                        }
                        if (['a', 'aa', 'i'].includes(CHAT_SETTINGS['chat-sound']) && (!this.chat_open || !this.tab_active || this.dashboard)) {
                            this.audio.play();
                        }
                        $(main).find('.sb-user-conversations').html(activeUser().getConversationsCode());
                    }
                });
            }
        },

        // Generate the conversation code and display it
        populate: function () {
            if (this.conversation != false) {
                let code = '';
                let notify = $(chat).find(' > .sb-notify-message');
                for (var i = 0; i < this.conversation.messages.length; i++) {
                    code += this.conversation.messages[i].getCode();
                }

                $(chat).html((notify.length ? $(notify)[0].outerHTML : '') + code);
                if (!this.dashboard) {
                    this.scrollBottom();

                }
            } else if (activeUser() != false && !activeUser().isConversationsEmpty()) {
                this.showDashboard();
            }
        },

        // Populate the dashboard with all the updated conversations
        populateConversations: function (onSuccess = false) {
            if (activeUser() != false) {
                activeUser().getConversations((response) => {
                    let count = response.length;
                    if (count) {
                        this.datetime_last_message = response[0]['messages'][0].get('creation_time');
                        for (var i = 0; i < count; i++) {
                            if (response[i].get('conversation_status_code') == 1) {
                                this.updateNotifications('add', response[i].id);
                            }
                        }
                        $(main).removeClass('sb-no-conversations');
                        $(main).find('.sb-user-conversations').html(activeUser().getConversationsCode());
                    }
                    if ((!this.initialized || force_action == 'open-conversation') && count == 1 && !this.isInitDashboard() && !storage('open-conversation')) {
                        this.openConversation(activeUser().conversations[0].id);
                        if (force_action == 'open-conversation') force_action = '';
                    }
                    if (onSuccess !== false) {
                        onSuccess(response);
                    }
                    this.finalizeInit();
                });
            }
        },

        // Create a new conversation and optionally send the first message
        newConversation: function (status_code, user_id = -1, message = '', attachments = [], department = null, agent_id = null, onSuccess = false) {
            if (activeUser() != false) {
                SBF.ajax({
                    function: 'new-conversation',
                    status_code: status_code,
                    title: tickets ? $(main).find('.sb-ticket-title input').val() : null,
                    department: SBF.null(department) ? this.default_department : department,
                    agent_id: SBF.null(agent_id) ? this.default_agent : agent_id,
                    routing: CHAT_SETTINGS['routing']
                }, (response) => {
                    if (SBF.errorValidation(response, 'user-not-found')) {
                        this.addUserAndLogin(() => {
                            this.newConversation(status_code, user_id, message, attachments, department, agent_id, onSuccess);
                        });
                        return;
                    }
                    let conversation = new SBConversation([], response['details']);
                    this.setConversation(conversation);
                    if (message != '' || attachments.length > 0) {
                        this.sendMessage(user_id, message, attachments, false, (tickets ? { 'skip-dialogflow': true } : false));
                    }
                    if (user_id != bot_id) {
                        this.queue(conversation.id);
                    }
                    if (onSuccess !== false) {
                        onSuccess(conversation);
                    }
                    SBF.event('SBNewConversationCreated', conversation);
                });
            } else {
                SBF.error('activeUser() not setted', 'SBChat.newConversation');
            }
        },

        // Set an existing conversation as active conversation
        setConversation: function (conversation) {
            if (conversation instanceof SBConversation) {
                this.conversation = conversation;
                this.datetime_last_message_conversation = this.conversation.getLastMessage() == false ? '2000-01-01 00:00:00' : this.conversation.getLastMessage().get('creation_time');
                if (conversation.id != this.conversation.id) {
                    this.queue(conversation.id);
                }
                storage('open-conversation', conversation.id);
                SBF.event('SBActiveConversationChanged', conversation);
            } else {
                SBF.error('Value not of type SBConversation', 'SBChat.setConversation');
            }
        },

        // Manage all the queue functionalities
        queue: function (conversation_id) {
            if (conversation_id == 'clear') {
                $(main).removeClass('sb-notify-active');
                $(chat).find(' > .sb-notify-message').remove();
                clearInterval(this.queue_interval);
                this.queue_interval = false;
                storage('queue', '');
                return;
            }
            if (!admin && CHAT_SETTINGS['queue']) {
                SBF.ajax({
                    function: 'queue',
                    conversation_id: conversation_id,
                    department: this.conversation.get('department')
                }, (response) => {
                    $(chat).find(' > .sb-notify-message').remove();
                    if (response == 0) {
                        this.sendMessage(bot_id, sb_(CHAT_SETTINGS['queue-message-success'] == '' ? 'It\'s your turn! An agent will reply to you shortly.' : CHAT_SETTINGS['queue-message-success']));
                        this.queue('clear');
                    } else {
                        let time = (CHAT_SETTINGS['queue-response-time'] == '' ? 5 : parseInt(CHAT_SETTINGS['queue-response-time'])) * response;
                        let text = sb_(CHAT_SETTINGS['queue-message'] == '' ? 'Please wait for an agent. You are number {position} in the queue. Your waiting time is approximately {minutes} minutes.' : CHAT_SETTINGS['queue-message']).replace('{position}', '<b>' + response + '</b>').replace('{minutes}', '<b>' + time + '</b>');
                        $(chat).prepend(`<div class="sb-notify-message sb-rich-cnt"><div class="sb-cnt"><div class="sb-message">${text}</div></div></div>`);
                        if (this.queue_interval === false) {
                            this.queue_interval = setInterval(() => { this.queue(conversation_id) }, 10000);
                            $(main).addClass('sb-notify-active');
                            storage('queue', conversation_id);
                        }
                    }
                    SBF.event('SBQueueUpdate', response);
                });
            }
        },

        // Get the departments details and generate the department code
        getDepartmentCode(department_id, onSuccess) {
            if (this.departments != false) {
                if (department_id == 'all') {
                    let code = '';
                    for (var key in this.departments) {
                        this.getDepartmentCode(this.departments[key]['id'], (response) => { code += response; });
                    };
                    onSuccess(code);
                } else {
                    onSuccess(`<div data-color="${this.departments[department_id]['color']}">${this.departments[department_id]['image'] == '' ? `<span></span>` : `<img src="${this.departments[department_id]['image']}" />`}<div>${this.departments[department_id]['name']}<div></div>`);
                }
            } else {
                SBF.ajax({
                    function: 'get-departments'
                }, (response) => {
                    if (response != false) {
                        this.departments = response;
                        this.getDepartmentCode(department_id, onSuccess);
                    }
                });
            }
        },

        // Start and stop the real time check of new messages
        startRealTime: function () {
            if (SBPusher.active) return;
            this.stopRealTime();
            this.real_time = setInterval(() => {
                if (!admin || (admin && this.user_online)) {
                    this.update();
                }
                this.typing(admin ? (activeUser() != false ? activeUser().id : -1) : this.agent_id, 'check');
            }, 1000);
        },

        stopRealTime: function () {
            clearInterval(this.real_time);
        },

        // Check if the agent is online and set the online status of the active user
        updateUsersActivity: function () {
            if (activeUser() != false) {
                SBF.updateUsersActivity(activeUser().id, this.agent_id, (response) => {
                    if (!this.typing_settings['typing']) {
                        if (response == 'online' || this.agent_id == bot_id) {
                            $(chat_status).addClass('sb-status-online').html(sb_('Online'));
                            this.agent_online = this.agent_id != bot_id;
                        } else {
                            $(chat_status).removeClass('sb-status-online').html(sb_('Away'));
                            this.agent_online = false;
                        }
                    }
                });
            }
        },

        // Show the loading icon and put the chat in busy mode
        busy: function (value) {
            $(chat_editor).find('.sb-loader').sbActivate(value);
            this.is_busy = value;
            SBF.event('SBBusy', value);
        },

        // Display the top header of the agent and hide the initial one
        headerAgent: function () {
            if (!admin && !tickets && !this.dashboard && this.conversation != false && (this.agent_id == -1 || (this.conversation.getLastMessage() != false && SBF.isAgent(this.conversation.getLastMessage().get('user_type')) && this.conversation.getLastMessage().get('user_id') != this.agent_id))) {
                let agent = this.lastAgent();
                if (agent != false) {
                    this.agent_id = agent['user_id'];
                    this.headerReset();
                    $(chat_header).addClass('sb-header-agent').attr('data-agent-id', this.agent_id).html(`<div class="sb-dashboard-btn sb-icon-arrow-left"></div><div class="sb-profile"><img src="${agent['profile_image']}" /><div><span class="sb-name">${agent['full_name']}</span><span class="sb-status">${sb_('Away')}</span></div></div>`);
                    chat_status = $(chat_header).find('.sb-status');
                    this.updateUsersActivity();
                }
            }
        },

        headerReset: function () {
            if (this.start_header == false) {
                this.start_header = [$(chat_header).html(), $(chat_header).attr('class')];
            }
            $(chat_header).removeClass('sb-header-main sb-header-brand sb-header-agent sb-header-minimal');
            this.main_header = false;
        },

        // Return the last agent of the active conversation
        lastAgent: function (bot = true) {
            let agent = false;
            if (this.conversation != false) {
                for (var i = this.conversation.messages.length - 1; i > -1; i--) {
                    let user_type = this.conversation.messages[i].get('user_type');
                    if (SBF.isAgent(user_type) && (bot || (!bot && user_type != 'bot'))) {
                        agent = { user_id: this.conversation.messages[i].get('user_id'), full_name: this.conversation.messages[i].get('full_name'), profile_image: this.conversation.messages[i].get('profile_image') };
                        break;
                    }
                }
            }
            return agent;
        },

        // Scroll the chat to the bottom
        scrollBottom: function (top = false) {
            setTimeout(() => {
                $(chat_scroll_area).scrollTop(top ? 0 : $(chat_scroll_area)[0].scrollHeight);
                this.scrollHeader();
            }, 20);
        },

        // Dashboard header animation
        scrollHeader: function () {
            if (this.main_header && this.dashboard) {
                let scroll = $(chat_scroll_area).scrollTop();
                if (scroll > -1 && scroll < 1000) {
                    $(chat_header).find('.sb-content').css({ 'opacity': (1 - (scroll / 500)), 'top': (scroll / 10 * -1) + 'px' });
                };
            }
        },

        // Display the dashboard area 
        showDashboard: function () {
            if (!admin && !tickets) {
                $(main).addClass('sb-dashboard-active');
                $(chat_header).removeClass('sb-header-agent');
                this.hidePanel()
                if (this.start_header != false) {
                    $(chat_header).html(this.start_header[0]).addClass(this.start_header[1]);
                }
                $(chat_scroll_area).find(' > div').sbActivate(false);
                $(main).find('.sb-dashboard').sbActivate();
                this.populateConversations();
                this.conversation = false;
                this.agent_id = -1;
                this.stopRealTime();
                this.dashboard = true;
                this.main_header = true;
                this.scrollBottom(true);
                SBF.event('SBDashboard');
            }
        },

        // Hide the dashboard area
        hideDashboard: function () {
            if (!admin && !tickets) {
                $(chat).sbActivate();
                $(main).removeClass('sb-dashboard-active').find('.sb-dashboard').sbActivate(false);
                this.dashboard = false;
                this.headerAgent();
                this.scrollHeader(0);
                if (this.chat_open) {
                    this.startRealTime();
                }
                SBF.event('SBDashboardClosed');
            }
        },

        // Show a chat panel
        showPanel: function (name, title) {
            if (tickets) return SBTickets.showPanel(name, title);
            let panel = $(chat_scroll_area).find(' > .sb-panel-' + name);
            if (panel.length) {
                $(chat_scroll_area).find(' > div').sbActivate(false);
                $(panel).sbActivate();
                if (this.start_header == false) {
                    this.start_header = [$(chat_header).html(), $(chat_header).attr('class')];
                }
                $(chat_header).attr('class', 'sb-header sb-header-panel').html(`${sb_(title)}<div class="sb-dashboard-btn sb-icon-close"></div>`);
                $(main).addClass('sb-panel-active');
                this.dashboard = true;
            }
            SBF.event('SBPanelActive', name);
        },

        hidePanel: function () {
            $(main).removeClass('sb-panel-active');
            $(chat_header).removeClass('sb-header-panel');
        },

        // Clear the conversation area and the active conversation
        clear: function () {
            this.conversation = false;
            $(chat).html('');
        },

        // Update the red notification counter of the chat
        updateNotifications: function (action = 'add', conversation_id) {
            if (action == 'add' && !this.notifications.includes(conversation_id)) {
                this.notifications.push(conversation_id);
            }
            if (action == 'remove') {
                for (var i = 0; i < this.notifications.length; i++) {
                    if (this.notifications[i] == conversation_id) {
                        this.notifications.splice(i, 1);
                        if (['1', '2', 1, 2].includes(this.conversation.get('conversation_status_code'))) {
                            this.setConversationStatus(0);
                        }
                        break;
                    }
                }
            }
            let count = this.notifications.length;
            $(main).find('.sb-chat-btn span').attr('data-count', count).html(count > -1 ? count : 0);
            SBF.event('SBNotificationsUpdate', { action: action, conversation_id: conversation_id });
        },

        // Set the active conversation status
        setConversationStatus: function (status_code) {
            if (this.conversation != false) {
                SBF.ajax({
                    function: 'update-conversation-status',
                    conversation_id: this.conversation.id,
                    status_code: status_code
                }, () => {
                    this.conversation.set('conversation_status_code', status_code);
                    SBF.event('SBActiveConversationStatusUpdated', { conversation_id: this.conversation.id, status_code: status_code });
                });
                return true;
            }
            return false;
        },

        // Typing status
        typing: function (user_id = -1, action = 'check') {
            if (this.conversation) {
                let valid = this.agent_online || (admin && this.user_online);
                if (action == 'check' && !SBPusher.active && user_id != -1 && user_id != bot_id && valid) {
                    SBF.ajax({
                        function: 'is-typing',
                        user_id: user_id,
                        conversation_id: this.conversation.id
                    }, (response) => {
                        if (response && !this.typing_settings['typing']) {
                            this.typing(-1, 'start');
                        } else if (!response && this.typing_settings['typing']) {
                            this.typing(-1, 'stop');
                        }
                    });
                } else if (action == 'set' && valid) {
                    if (SBPusher.active) {
                        SBF.debounce(() => {
                            SBPusher.trigger('client-typing', { user_id: admin ? SB_ACTIVE_AGENT['id'] : activeUser().id, conversation_id: this.conversation.id });
                        }, '#2');
                    } else {
                        if (!this.typing_settings['sent']) {
                            this.typing_settings['sent'] = true;
                            SBF.ajax({
                                function: 'set-typing',
                                user_id: user_id,
                                conversation_id: this.conversation.id
                            });
                            this.typing(user_id, 'set');
                        } else {
                            clearTimeout(this.typing_settings['timeout']);
                            this.typing_settings['timeout'] = setTimeout(() => {
                                SBF.ajax({
                                    function: 'set-typing',
                                    user_id: user_id,
                                    conversation_id: -1
                                }, () => {
                                    this.typing_settings['sent'] = false;
                                });
                            }, 2000);
                        }
                    }
                } else if (action == 'start' || action == 'stop') {
                    let start = action == 'start';
                    if (!admin) {
                        if (start) {
                            $(chat_status).addClass('sb-status-typing').html(sb_('Typing'));
                        } else {
                            let online = this.agent_online || this.agent_id == bot_id;
                            $(chat_status).removeClass('sb-status-typing').html(sb_(online ? 'Online' : 'Away'));
                            if (online) $(chat_status).addClass('sb-status-online');
                        }
                    }
                    this.typing_settings['typing'] = start;
                    SBF.event('SBTyping', start);
                }
            }
        },

        // Articles
        showArticles: function (id = -1) {
            let panel = tickets ? $(main).find('.sb-panel-main .sb-panel') : $(chat_scroll_area).find(' > .sb-panel-articles');
            let code = '';
            $(panel).html('').sbLoading(true);
            this.showPanel('articles', CHAT_SETTINGS['articles-title'] == '' ? 'Help Center' : CHAT_SETTINGS['articles-title']);
            this.getArticles(id, (articles) => {
                if (id == -1) {
                    for (var i = 0; i < articles.length; i++) {
                        code += `<div data-id="${articles[i]['id']}"><div>${articles[i]['title']}</div><span>${articles[i]['content']}</span></div>`;
                    }
                    $(panel).html(`<div class="sb-articles">${code}</div>`);
                } else {
                    if (articles !== false) {
                        $(panel).html(`<div data-id="${articles['id']}" class="sb-article"><div class="sb-title">${articles['title']}</div><div class="sb-content">${articles['content'].replace(/(?:\r\n|\r|\n)/g, '<br>')}</div>${SBF.null(articles['link']) ? '' : `<a href="${articles['link']}" target="_blank" class="sb-btn-text"><i class="sb-icon-plane"></i>${sb_('Read more')}</a>`}</div>`);
                    }
                }
                $(panel).sbLoading(false);
                SBF.event('SBArticles', { id: id, articles: articles });
            });
        },

        getArticles: function (id = -1, onSuccess = false) {
            if (this.articles === false || id != -1) {
                SBF.ajax({
                    function: 'get-articles',
                    id: id
                }, (response) => {
                    if (id == -1) {
                        this.articles = response;
                    }
                    if (onSuccess === false) {
                        return response;
                    } else {
                        onSuccess(response);
                    }
                });
            } else {
                if (onSuccess === false) {
                    return this.articles;
                } else {
                    onSuccess(this.articles);
                }
            }
        },

        searchArticles: function (search, btn) {
            if (search != '') {
                $(btn).sbLoading(true);
                SBF.ajax({
                    function: 'search-articles',
                    search: search
                }, (articles) => {
                    let code = '';
                    let count = articles.length;
                    if (count == 0) {
                        code += `<p class="sb-no-results">${sb_('No articles found.')}</p>`;
                    } else {
                        for (var i = 0; i < articles.length; i++) {
                            code += `<div data-id="${articles[i]['id']}"><div>${articles[i]['title']}</div><span>${articles[i]['content']}</span></div>`;
                        }
                    }
                    $(main).find('.sb-dashboard-articles .sb-articles').html(code);
                    $(btn).sbLoading(false);
                });
            }
        },

        // Emoji
        categoryEmoji: function (category) {
            let list = this.emoji_options['list'];
            if (category == 'all') {
                this.emoji_options['list_now'] = list;
            } else {
                this.emoji_options['list_now'] = [];
                for (var i = 0; i < list.length; i++) {
                    if (list[i]['category'].startsWith(category)) {
                        this.emoji_options['list_now'].push(list[i]);
                    }
                }
            }
            this.emoji_options['range'] = 0;
            this.populateEmoji(0);
            this.populateEmojiBar();
        },

        mouseWheelEmoji: function (e) {
            let range = this.emoji_options['range'];
            if (sbDelta(e) > 0 || (mobile && typeof e.originalEvent.changedTouches !== ND && this.emoji_options['touch'] < e.originalEvent.changedTouches[0].clientY)) {
                range -= (range < 1 ? 0 : 1);
            } else {
                range += (range > this.emoji_options['range_limit'] ? 0 : 1);
            }
            $(chat_emoji).find('.sb-emoji-bar > div').sbActivate(false).eq(range).sbActivate();
            this.emoji_options['range'] = range;
            this.populateEmoji(range);
            e.preventDefault();
        },

        insertEmoji: function (emoji) {
            if (emoji.indexOf('.svg') > 0) {
                emoji = $.parseHTML(emoji)[0]['alt'];
            }
            this.insertText(emoji);
            $(chat_emoji).sbTogglePopup();
        },

        showEmoji: function (button) {
            if ($(chat_emoji).sbTogglePopup(button)) {
                if (!admin) {
                    $(chat_emoji).css({ left: $(chat_editor).offset().left + (tickets ? 68 : 20), top: $(chat_editor).offset().top - window.scrollY - (tickets ? $(chat_editor).height() - 330 : 304) });
                }
                if ($(chat_emoji).find('.sb-emoji-list > ul').html() == '') {
                    jQuery.ajax({
                        method: 'POST',
                        url: SB_AJAX_URL,
                        data: {
                            function: 'emoji'
                        }
                    }).done((response) => {
                        this.emoji_options['list'] = JSON.parse(response);
                        this.emoji_options['list_now'] = this.emoji_options['list'];
                        this.populateEmoji(0);
                        this.populateEmojiBar();
                    });
                }
                SBF.deselectAll();
            }
        },

        populateEmoji: function (range) {
            let code = '';
            let per_page = mobile ? 42 : 48;
            let limit = range * per_page + per_page;
            let list_now = this.emoji_options['list_now'];
            if (limit > list_now.length) limit = list_now.length;
            this.emoji_options['range_limit'] = list_now.length / per_page - 1;
            this.emoji_options['range'] = range;
            for (var i = (range * per_page); i < limit; i++) {
                code += `<li>${list_now[i]['char']}</li>`;
            }
            $(chat_emoji).find('.sb-emoji-list').html(`<ul>${code}</ul>`);
        },

        populateEmojiBar: function () {
            let code = '<div class="sb-active"></div>';
            let per_page = mobile ? 42 : 49;
            for (var i = 0; i < this.emoji_options['list_now'].length / per_page - 1; i++) {
                code += '<div></div>';
            }
            this.emoji_options['range'] = 0;
            $(chat_emoji).find('.sb-emoji-bar').html(code);
        },

        clickEmojiBar: function (item) {
            let range = $(item).index();
            this.populateEmoji(range);
            this.emoji_options['range'] = range;
            $(chat_emoji).find('.sb-emoji-bar > div').sbActivate(false).eq(range).sbActivate();
        },

        searchEmoji: function (search) {
            SBF.search(search, () => {
                if (search.length > 1) {
                    let list = this.emoji_options['list'];
                    let list_now = [];
                    for (var i = 0; i < list.length; i++) {
                        if (list[i]['category'].toLowerCase().includes(search) || list[i]['name'].toLowerCase().includes(search)) {
                            list_now.push(list[i]);
                        }
                    }
                    this.emoji_options['list_now'] = list_now;
                } else {
                    this.emoji_options['list_now'] = this.emoji_options['list'];
                }
                this.emoji_options['range'] = 0;
                this.populateEmoji(0);
                this.populateEmojiBar();
            });
        },

        // Editor methods
        textareaChange: function (textarea) {
            let value = $(textarea).val();

            // Saved replies
            if (admin) {
                SBAdmin.conversations.savedReplies(textarea, value);
            }

            // Typing
            if (value != '') {
                this.typing((admin && !SBPusher.active ? SB_ACTIVE_AGENT['id'] : activeUser().id), 'set');
                this.activateBar();
            } else {
                this.activateBar(false);
            }
        },

        insertText: function (text) {
            let textarea = $(chat_textarea).get(0);
            let index = 0;
            if (this.dashboard) return false;
            if ('selectionStart' in textarea) {
                index = textarea.selectionStart;
            } else if ('selection' in document) {
                textarea.focus();
                let selection = document.selection.createRange();
                var selection_length = document.selection.createRange().text.length;
                selection.moveStart('character', -textarea.value.length);
                index = selection.text.length - selection_length;
            }
            $(textarea).val($(textarea).val().substr(0, index) + text + $(textarea).val().substr(index));
            $(textarea).focus();
            $(textarea).manualExpandTextarea();
            this.activateBar();
        },

        enabledAutoExpand: function () {
            if (chat_textarea.length) $(chat_textarea).autoExpandTextarea();
        },

        // Privacy message
        privacy: function () {
            SBF.ajax({
                function: 'get-block-setting',
                value: 'privacy'
            }, (response) => {
                $(chat_scroll_area).append(`<div class="sb-privacy sb-init-form" data-decline="${response['decline']}"><div class="sb-title">${response['title']}</div><div class="sb-text">${response['message']}</div>` + (response['link'] != '' ? `<a target="_blank" href="${response['link']}">${response['link-name']}</a>` : '') + `<div class="sb-buttons"><a class="sb-btn sb-approve">${response['btn-approve']}</a><a class="sb-btn sb-decline">${response['btn-decline']}</a></div></div>`);
                this.finalizeInit();
                SBF.event('SBPrivacy');
            });
            if (!this.dashboard) {
                this.showDashboard();
            }
            this.dashboard = true;
            $(main).addClass('sb-init-form-active');
        },

        // Popup message 
        popup: function (close = false, content = false) {
            let popup = $(main).find('.sb-popup-message');
            if (popup.length) {
                $(popup).remove();
            }
            if (close) {
                storage('popup', true);
                return;
            }
            if (!this.chat_open) {
                if (content == false) {
                    SBF.ajax({
                        function: 'get-block-setting',
                        value: 'popup'
                    }, (response) => {
                        setTimeout(() => {
                            if (!this.chat_open) {
                                this.popup(close, response);
                            }
                        }, 1000);
                    });
                } else {
                    $(main).append(`<div class="sb-popup-message">` + ('image' in content && content['image'] != '' ? `<img src="${content['image']}" />` : '') + ('title' in content && content['title'] != '' ? `<div class="sb-top">${content['title']}</div>` : '') + `<div class="sb-text">${content['message']}</div><div class="sb-icon-close"></div></div>`);
                    SBF.event('SBPopup', content);
                }
            }
        },

        // Follow up message
        followUp: function () {
            if (this.followUpCheck()) {
                if (timeout != false) {
                    clearTimeout(timeout);
                }
                timeout = setTimeout(() => {
                    if (this.followUpCheck()) {
                        let settings = CHAT_SETTINGS['follow'];
                        this.sendMessage(bot_id, `[email id="sb-follow-up-form" title="${settings['title']}" message="${settings['message']}" placeholder="${settings['placeholder']}" name="${settings['name']}" last-name="${settings['last-name']}" success="${settings['success']}"]`);
                        this.scrollBottom();
                        SBF.storageTime('email');
                        SBF.event('SBFollowUp');
                    }
                }, SBF.null(CHAT_SETTINGS['follow']['delay']) ? (CHAT_SETTINGS['office-hours'] || agents_online ? 15000 : (SBApps.dialogflow.active() ? 8000 : 5000)) : parseInt(CHAT_SETTINGS['follow']['delay']));
            }
        },

        followUpCheck: function () {
            return !admin && this.conversation != false && activeUser() != false && activeUser().get('email') == '' && SBF.storageTime('email', 24);
        },

        // Welcome message
        welcome: function () {
            if ((CHAT_SETTINGS['welcome-trigger'] != 'open' || this.chat_open) && CHAT_SETTINGS['welcome'] && !storage('welcome') && activeUser()) {
                SBF.ajax({
                    function: 'get-block-setting',
                    value: 'welcome'
                }, (response) => {
                    setTimeout(() => {
                        if (CHAT_SETTINGS['dialogflow-welcome']) {
                            if (this.conversation === false) {
                                this.newConversation(3, -1, '', [], null, null, function () { SBApps.dialogflow.welcome() });
                            } else {
                                SBApps.dialogflow.welcome();
                            }
                        } else {
                            this.sendMessage(bot_id, response['message'], [], false, false, 3);
                        }
                        if (response['open']) {
                            this.start();
                        }
                        if (response['sound']) {
                            this.audio.play();
                        }
                        this.skip = true;
                        SBF.event('SBWelcomeMessage');
                    }, parseInt(tickets ? 0 : CHAT_SETTINGS['welcome-delay']));
                    storage('welcome', true);
                });
            }
        },

        // Subscribe message
        subscribe: function () {
            if (CHAT_SETTINGS['subscribe'] && !storage('subscribe') && activeUser() != false && SBF.null(activeUser().get('email')) && activeUser()) {
                setTimeout(() => {
                    SBF.ajax({
                        function: 'get-block-setting',
                        value: 'subscribe'
                    }, (response) => {
                        this.sendMessage(bot_id, response['message'], [], false, { event: 'open-chat' }, 3);
                        if (response['sound']) {
                            this.audio.play();
                        }
                        this.skip = true;
                        storage('subscribe', true);
                    });
                }, parseInt(CHAT_SETTINGS['subscribe-delay']));
            }
        },

        // Send Slack message
        slackMessage: function (user_id, full_name, profile_image, message, attachments = []) {
            if (this.conversation == false) return false;
            let conversation_id = this.conversation.id;
            SBF.ajax({
                function: 'send-slack-message',
                user_id: user_id,
                full_name: full_name,
                profile_image: profile_image,
                conversation_id: conversation_id,
                message: message,
                attachments: attachments,
                channel: this.slack_channel[0] == activeUser().id ? this.slack_channel[1] : -1
            }, (response) => {
                this.slack_channel = [activeUser().id, response[1]];
                SBF.event('SBSlackMessageSent', { message: message, conversation_id: conversation_id, slack_channel: response[1] });
            });
        },

        // Delete message
        deleteMessage: function (message_id) {
            SBF.ajax({
                function: 'delete-message',
                message_id: message_id
            }, () => {
                this.conversation.deleteMessage(message_id);
                $(chat).find(`[data-id="${message_id}"]`).remove();
                SBF.event('SBMessageDeleted', message_id);
            });
        },

        // Registration form
        registration: function (check = false, type = CHAT_SETTINGS['registration-required']) {
            if (check) return CHAT_SETTINGS['registration-required'] != '' && (!CHAT_SETTINGS['registration-offline'] || !agents_online) && (!CHAT_SETTINGS['registration-timetable'] || !CHAT_SETTINGS['office-hours']) && (activeUser() === false || ['visitor', 'lead'].includes(activeUser().type));
            $(chat_scroll_area).append(SBRichMessages.generate({}, CHAT_SETTINGS['registration-link'] != '' ? 'login' : type, 'sb-init-form'));
            if (!this.dashboard) {
                this.showDashboard();
            }
            this.dashboard = true;
            this.finalizeInit();
            $(main).addClass('sb-init-form-active');
        },

        // Display the send button
        activateBar: function (show = true) {
            $(chat_editor).find('.sb-bar').sbActivate(show);
        },

        // Shortcut for add user and login function
        addUserAndLogin: function (onSuccess = false) {

            if (typeof gst_user_email !== ND) {
                
                SBF.ajax({
                    function: 'auto-login',
                    user_email: gst_user_email,
                    user_name: gst_name_chat,
                }, (response) => {
                    SBF.loginCookie(response[1]);
                    activeUser(new SBUser(response[0]));
                    if (onSuccess !== false) {
                        onSuccess(response);
                    }
                    this.populateConversations();
                    setTimeout(function () {
                        SBChat.openConversation($('.sb-user-conversations').find('li').attr('data-conversation-id')); 
                    }, 500);          
                });
                
            } else {
            
                SBF.ajax({
                    function: 'add-user-and-login',
                    login_app: SBApps.login()
                }, (response) => {
                    SBF.loginCookie(response[1]);
                    activeUser(new SBUser(response[0]));
                    if (onSuccess !== false) {
                        onSuccess(response);
                    }
                });
            
            }
        },

        // Check if the dashboard must be showed
        isInitDashboard: function () {
            return CHAT_SETTINGS['init-dashboard'] || (activeUser() != false && activeUser().conversations.length > 1);
        },

        // Upload response
        uploadResponse: function (response) {
            response = JSON.parse(response);
            if (response[0] == 'success') {
                if (response[1] == 'extension_error') {
                    SBF.dialog('The file you are trying to upload has an extension that is not allowed.', 'info');
                } else if ($(upload_target).hasClass('sb-input-image')) {
                    $(upload_target).find('.image').attr('data-value', '').css('background-image', '');
                    setTimeout(() => {
                        $(upload_target).find('.image').attr('data-value', response[1]).css('background-image', `url("${response[1]}?v=${SBF.random()}")`).append('<i class="sb-icon-close"></i>');
                        upload_target = false;
                    }, 500);
                } else {
                    let name = response[1].substr(response[1].lastIndexOf('/') + 1);
                    $(chat_editor).find('.sb-attachments').append(`<div data-name="${name}" data-value="${response[1]}">${name}<i class="sb-icon-close"></i></div>`);
                    SBChat.activateBar();
                }
            } else {
                SBF.error(response[1], 'sb-upload-files.change');
            }
            this.busy(false);
        }
    }
    window.SBChat = SBChat;

    /* 
    * ----------------------------------------------------------
    * # RICH MESSAGES
    * ----------------------------------------------------------
    */

    var SBRichMessages = {
        rich_messsages: {
            'email': '',
            'button': '',
            'video': '',
            'image': '',
            'woocommerce-button': '',
            'chips': '<div class="sb-buttons">[options]</div>',
            'buttons': '<div class="sb-buttons">[options]</div>',
            'select': '<div class="sb-select"><p></p><ul>[options]</ul></div>',
            'list': '<div class="sb-text-list">[values]</div>',
            'list-image': '<div class="sb-image-list">[values]</div>',
            'table': '<table><tbody>[header][values]</tbody></table>',
            'inputs': '<div class="sb-form">[values]</div>',
            'rating': `<div class="sb-rating"><div class="sb-input sb-input-textarea"><textarea></textarea></div><div><div data-rating="positive" class="sb-submit"><i class="sb-icon-like"></i>${sb_('Good')}</div><div data-rating="negative" class="sb-submit"><i class="sb-icon-dislike"></i>${sb_('Bad')}</div></div></div>`,
            'card': '<div class="sb-card">[settings]</div>',
            'share': '<div class="sb-social-buttons">[settings]</div>',
            'slider': '<div class="sb-slider"><div>[items]</div></div><div class="sb-slider-arrow sb-icon-arrow-left[class]"></div><div class="sb-slider-arrow sb-icon-arrow-right sb-active[class]"></div>',
            'slider-images': '<div class="sb-slider sb-slider-images"><div>[items]</div></div><div class="sb-slider-arrow sb-icon-arrow-left[class]"></div><div class="sb-slider-arrow sb-icon-arrow-right sb-active[class]"></div>'
        },
        cache: {},

        // Generate a rich message
        generate: function (settings, name, css = '') {
            let content;
            let next = true;
            let id = 'id' in settings ? settings['id'] : SBF.random();

            // Check if the rich message exist
            if (name in this.rich_messsages) {
                content = this.rich_messsages[name];
            } else if (name in this.cache) {
                content = this.cache[name];
            } else if (!SBF.null(CHAT_SETTINGS) && CHAT_SETTINGS['rich-messages'].includes(name)) {
                if (!('id' in settings)) id = name;
                content = '<div class="sb-rich-loading sb-loading"></div>';
                SBF.ajax({
                    function: 'get-rich-message',
                    name: name
                }, (response) => {
                    response = this.initInputs(response);
                    if (name == 'timetable') response = this.timetable(response);
                    $(main).find(`.sb-rich-message[id="${id}"]`).html(`<div class="sb-content">${response}</div>`);
                    this.cache[name] = response;
                    SBChat.scrollBottom(SBChat.dashboard);
                });
                next = false;
            } else {
                return false;
            }

            // Generate the rich message
            if (next) {
                let options;
                let code = '';
                switch (name) {
                    case 'email':
                        let user_name = 'last-name' in settings ? activeUser().get('first_name') : activeUser().name;
                        let last_name = activeUser().get('last_name');
                        let email = activeUser().get('email');
                        if (last_name.charAt(0) == '#') {
                            user_name = last_name = '';
                        }
                        content = (settings['name'] === 'true' ? `<div id="first_name" data-type="text" class="sb-input sb-input-text"><span class="${user_name == '' ? '' : 'sb-active sb-filled'}">${sb_(settings['last-name'] === 'true' ? 'First name' : 'Name')}</span><input value="${user_name}" autocomplete="false" type="text" required></div>` : '') + (settings['last-name'] == 'true' ? `<div id="last_name" data-type="text" class="sb-input sb-input-text"><span class="${last_name == '' ? '' : 'sb-active sb-filled'}">${sb_('Last name')}</span><input value="${last_name}" autocomplete="false" type="text" required></div>` : '') + `<div id="email" data-type="email" class="sb-input sb-input-btn"><span class="${email == '' ? '' : 'sb-active sb-filled'}">${sb_('placeholder' in settings ? settings['placeholder'] : 'Email')}</span><input value="${email}" autocomplete="off" type="email" required><div class="sb-submit sb-icon-arrow-right"></div></div>`;
                        break;
                    case 'image':
                        content = `<div class="sb-image"><img src="${settings['url']}"></div>`;
                        break;
                    case 'video':
                        content = `<iframe${'height' in settings ? ` height="${settings['height']}"` : ''} src="${settings['type'] == 'youtube' ? '//www.youtube.com/embed/' : '//player.vimeo.com/video/'}${settings['id']}" allowfullscreen></iframe>`;
                        break;
                    case 'select':
                        options = settings['options'].split(',');
                        for (var i = 0; i < options.length; i++) {
                            code += `<li data-value="${SBF.stringToSlug(options[i])}">${sb_(options[i])}</li>`;
                        }
                        content = content.replace('[options]', code);
                        break;
                    case 'chips':
                    case 'buttons':
                        options = settings['options'].split(',');
                        for (var i = 0; i < options.length; i++) {
                            code += `<div class="sb-btn sb-submit">${sb_(options[i])}</div>`;
                        }
                        content = content.replace('[options]', code);
                        break;
                    case 'button':
                        content = `<a href="${settings['link'].replace(/<i>/g, '_').replace(/<\/i>/g, '_')}"${'target' in settings ? ' target="_blank"' : ''} class="sb-rich-btn sb-btn${settings['style'] == 'link' ? '-text' : ''}">${sb_(settings['name'])}</a>`;
                        break;
                    case 'list':
                        options = settings['values'].split(',');
                        let list = name == 'list';
                        let list_double = list && options.length && options[0].indexOf(':') > 0;
                        if (list && !list_double) {
                            content = content.replace('sb-text-list', 'sb-text-list sb-text-list-single');
                        }
                        for (var i = 0; i < options.length; i++) {
                            code += list_double ? `<div><div>${sb_(options[i].split(':')[0])}</div><div>${sb_(options[i].split(':')[1])}</div></div>` : `<div>${$.trim(sb_(options[i]))}</div>`;
                        }
                        content = content.replace('[values]', code);
                        break;
                    case 'list-image':
                        options = settings['values'].split(',');
                        for (var i = 0; i < options.length; i++) {
                            let item = options[i].replace('://', '///').split(':');
                            code += `<div><div class="sb-thumb" style="background-image:url('${item[0].replace('///', '://')}')"></div><div class="sb-list-title">${item[1]}</div><div>${item[2]}</div></div>`;
                        }
                        content = content.replace('[values]', code);
                        break;
                    case 'table':
                        options = settings['header'].split(',');
                        code += '<tr>';
                        for (var i = 0; i < options.length; i++) {
                            code += `<th>${options[i]}</th>`;
                        }
                        code += '</tr>';
                        content = content.replace('[header]', code);
                        code = '';
                        options = settings['values'].split(',');
                        for (var i = 0; i < options.length; i++) {
                            let tds = options[i].split(':');
                            code += '<tr>';
                            for (var i = 0; i < tds.length; i++) {
                                code += `<td>${tds[i]}</td>`;
                            }
                            code += '</tr>';
                        }
                        content = content.replace('[values]', code);
                        break;
                    case 'inputs':
                        options = settings['values'].split(',');
                        for (var i = 0; i < options.length; i++) {
                            code += `<div id="${SBF.stringToSlug(options[i])}" data-type="text" class="sb-input sb-input-text"><span>${sb_(options[i])}</span><input autocomplete="false" type="text" required></div>`;
                        }
                        code += '<div class="sb-btn sb-submit">' + sb_('button' in settings ? settings['button'] : 'Send now') + '</div>';
                        content = content.replace('[values]', code);
                        break;
                    case 'card':
                        code = `${'image' in settings ? `<div class="sb-card-img" style="background-image:url(${settings['image']})"></div>` : ''}<div class="sb-card-header">${settings['header']}</div>${'extra' in settings ? `<div class="sb-card-extra">${settings['extra']}</div>` : ''}${'description' in settings ? `<div class="sb-card-description">${settings['description']}</div>` : ''}${'link' in settings ? `<a class="sb-card-btn" href="${settings['link']}">${sb_(settings['link-text'])}</a>` : ''}`;
                        content = content.replace('[settings]', code);
                        break;
                    case 'share':
                        let channels = ['fb', 'tw', 'li', 'wa', 'pi'];
                        let link = '';
                        for (var i = 0; i < channels.length; i++) {
                            if (channels[i] in settings) {
                                switch (channels[i]) {
                                    case 'fb':
                                        link = 'www.facebook.com/sharer.php?u=';
                                        break;
                                    case 'tw':
                                        link = 'twitter.com/intent/tweet?url=';
                                        break;
                                    case 'li':
                                        link = 'www.linkedin.com/sharing/share-offsite/?url=';
                                        break;
                                    case 'wa':
                                        link = 'web.whatsapp.com/send?text=';
                                        break;
                                    case 'pi':
                                        link = 'www.pinterest.com/pin/create/button/?url=';
                                        break;
                                }
                                code += `<div class="sb-${channels[i]} sb-icon-social-${channels[i]}" data-link="https://${link}${settings['link']}"></div>`;
                            }
                        }
                        content = content.replace('[settings]', code);
                        break;
                    case 'slider':
                        let count = 0;
                        for (var i = 1; i < 16; i++) {
                            if (('header-' + i) in settings) {
                                code += `<div>${('image-' + i) in settings ? `<div class="sb-card-img" style="background-image:url(${settings['image-' + i]})"></div>` : ''}<div class="sb-card-header">${settings['header-' + i]}</div>${('extra-' + i) in settings ? `<div class="sb-card-extra">${settings['extra-' + i]}</div>` : ''}${('description-' + i) in settings ? `<div class="sb-card-description">${settings['description-' + i]}</div>` : ''}${('link-' + i) in settings ? `<a class="sb-card-btn" href="${settings['link-' + i]}">${sb_(settings['link-text-' + i])}</a>` : ''}</div>`;
                                count++;
                            } else {
                                break;
                            }
                        }
                        content = content.replace('[items]', code).replace(/\[class\]/g, count == 1 ? ' sb-hide' : '');
                        break;
                    case 'slider-images':
                        if ('images' in settings) {
                            let images = settings['images'].split(',');
                            for (var i = 0; i < images.length; i++) {
                                code += `<div class="sb-card-img" data-value="${images[i]}" style="background-image:url(${images[i]})"></div>`;
                            }
                            content = content.replace(/\[class\]/g, images.length == 1 ? ' sb-hide' : '');
                        }
                        content = content.replace('[items]', code);
                        break;
                    case 'woocommerce-button':
                        settings['settings'] = `checkout:${settings['checkout']},coupon:${settings['coupon']}`;
                        content = `<a href="#" data-ids="${settings['ids']}" class="sb-rich-btn sb-btn">${settings['name']}</a>`;
                        break;
                }
            }
            return `<div id="${id}" data-type="${name}"${'disabled' in settings ? 'disabled="true"' : ''}${'settings' in settings ? ` data-settings="${settings['settings']}"` : ''}class="sb-rich-message sb-rich-${name} ${css}">` + ('title' in settings ? `<div class="sb-top">${sb_(settings['title'])}</div>` : '') + ('message' in settings ? `<div class="sb-text">${sb_(settings['message'])}</div>` : '') + `<div class="sb-content">${content}</div><div data-success="${'success' in settings ? settings['success'].replace(/"/g, '') : ''}" class="sb-info"></div></div>`;
        },

        // Function of built-in rich messages
        submit: function (area, type, element) {
            if (!admin && !$(element).sbLoading() && !this.is_busy) {
                let error = '';
                let shortcode = '';
                let parameters = {};
                let success = $(area).find('[data-success]').length ? $(area).find('[data-success]').attr('data-success') : '';
                let rich_message_id = $(area).closest('.sb-rich-message').attr('id');
                let message_id = $(area).closest('[data-id]').attr('data-id');
                let message = '';
                let payload = { 'rich-messages': {} };
                let user_settings = activeUser() == false ? { 'profile_image': ['', ''], 'first_name': ['', ''], 'last_name': ['', ''], 'email': ['', ''], 'password': ['', ''], 'user_type': ['', ''] } : { 'profile_image': [activeUser().image, ''], 'first_name': [activeUser().get('first_name'), ''], 'last_name': [activeUser().get('last_name'), ''], 'email': [activeUser().get('email'), ''], 'password': ['', ''], 'user_type': ['', ''] };
                let settings = {};
                let input = element;
                let dialogflow_response = '';
                let active_conversation = SBChat.conversation !== false;

                if (SBF.null(message_id)) {
                    message_id = -1;
                } else {
                    let item = SBChat.conversation.getMessage(message_id);
                    message = item.get('message');
                    payload = item.payload();
                    if (!('rich-messages' in payload)) {
                        payload['rich-messages'] = {};
                    }
                }
                if (!$(element).hasClass('sb-btn') && !$(element).hasClass('sb-select') && !$(element).hasClass('sb-submit')) {
                    input = $(element).closest('.sb-btn,.sb-select');
                }
                $(input).sbLoading(true);
                $(area).find('.sb-info').html('').sbActivate(false);

                switch (type) {
                    case 'email':
                        settings = SBForm.getAll(area);
                        if ('first_name' in settings) {
                            user_settings['user_type'] = ['user', ''];
                            if (!('last_name' in settings)) {
                                user_settings['last_name'] = ['', ''];
                            }
                        }
                        $.extend(user_settings, settings);
                        error = 'Please insert a valid email.' + ('first_name' in settings ? '' : ' All fields are required.');
                        success = success == '' ? success : sb_(success) + ` *${user_settings['email'][0]}*`;
                        payload['rich-messages'][rich_message_id] = { type: type, result: settings };
                        payload['event'] = 'update-user';
                        parameters = { function: 'update-user-and-message', settings: user_settings, payload: payload };
                        dialogflow_response = 'email-complete';
                        break;
                    case 'chips':
                    case 'select':
                    case 'buttons':
                        settings = SBF.escape($(element).html());
                        success = success == '' ? success : sb_(success) + ` *${settings}*`;
                        payload['rich-messages'][rich_message_id] = { type: type, result: settings };
                        parameters = { function: 'update-message', payload: payload };
                        dialogflow_response = settings;
                        if (type == 'chips') {
                            SBChat.sendMessage(activeUser().id, settings, [], false, { id: rich_message_id, event: 'chips-click', result: settings });
                            $(element).closest('.sb-content').remove();
                        }
                        break;
                    case 'inputs':
                        settings = SBForm.getAll(area);
                        error = 'All fields are required.';
                        if (success != '') {
                            success = sb_(success) + ' [list values="';
                            for (var key in settings) {
                                success += `${sb_(settings[key][1].replace(/:|,/g, ''))}:${settings[key][0].replace(/:|,/g, '')},`;
                            }
                            success = success.slice(0, -1) + '"]';
                        }
                        payload['rich-messages'][rich_message_id] = { type: type, result: settings };
                        parameters = { function: 'update-message', payload: payload };
                        dialogflow_response = JSON.stringify(settings);
                        break;
                    case 'registration':
                        let settings_extra = SBForm.getAll(area.find('.sb-form-extra'));
                        let payload_settings;
                        $.extend(user_settings, SBForm.getAll(area.find('.sb-form-main')));
                        payload_settings = $.extend({}, user_settings);
                        success = `${sb_(success == '' ? 'Your account details are as follows:' : success)} [list values="`;
                        for (var key in user_settings) {
                            let value = user_settings[key][0].replace(/:|,/g, '');
                            if (value != '') {
                                if (key == 'profile_image') {
                                    value = value.substr(value.lastIndexOf('/') + 1);
                                }
                                if (key == 'password' || key == 'password-check') {
                                    value = '********';
                                    payload_settings[key] = '********';
                                }
                                success += user_settings[key][1] == '' ? '' : `${sb_(user_settings[key][1].replace(/:|,/g, ''))}:${value},`;
                            }
                        }
                        for (var key in settings_extra) {
                            if (settings_extra[key][0] != '') {
                                success += `${sb_(settings_extra[key][1].replace(/:|,/g, ''))}:${settings_extra[key][0].replace(/:|,/g, '')},`;
                            }
                        }
                        user_settings['user_type'] = ['user', ''];
                        success = success.slice(0, -1) + '"]';
                        payload['rich-messages'][rich_message_id] = { type: type, result: { user: payload_settings, extra: settings_extra } };
                        payload['event'] = 'update-user';
                        parameters = { function: activeUser() != false ? 'update-user-and-message' : 'add-user-and-login', settings: user_settings, settings_extra: settings_extra, payload: payload };
                        error = SBForm.getRegistrationErrorMessage(area);
                        dialogflow_response = JSON.stringify(user_settings);
                        break;
                    case 'rating':
                        let rating = $(element).attr('data-rating');
                        success = `${sb_('Your rating is')} *${sb_(rating)}*. ${sb_(success == '' ? 'Thank you for your feedback!' : success)}`;
                        settings = { conversation_id: SBChat.conversation.id, agent_id: SBChat.agent_id, user_id: activeUser().id, message: area.find('textarea').val(), rating: (rating == 'positive' ? 1 : -1) };
                        payload['rich-messages'][rich_message_id] = { type: type, result: settings };
                        parameters = { function: 'set-rating', payload: payload, settings: settings };
                        dialogflow_response = rating;
                        break;

                }

                shortcode = message.substr(message.indexOf('[' + type))
                shortcode = shortcode.substr(0, shortcode.indexOf(']') + 1);

                if (error != '' && SBForm.errors(area)) {
                    SBForm.showErrorMessage(area, error);
                    $(input).sbLoading(false);
                    if (SBChat.dashboard || (active_conversation && SBChat.conversation.getLastMessage().id == message_id)) {
                        SBChat.scrollBottom();
                    }
                    return false;
                }
                if (success == '') {
                    let shortcode_settings = this.shortcode(shortcode);
                    let title = 'title' in shortcode_settings[1] ? `title="${shortcode_settings[1]['title']}"` : '';
                    let message = 'message' in shortcode_settings[1] ? `message="${shortcode_settings[1]['message']}"` : '';
                    let value = '';
                    if (['inputs', 'email'].includes(type)) {
                        for (var key in settings) {
                            value += settings[key][0] + ',';
                        }
                        value = `values="${value.slice(0, -1)}"`
                    } else {
                        value = `options="${settings}"`;
                    }
                    success = `[${type == 'email' ? 'inputs' : type} ${title} ${message} ${value} disabled="true"]`;
                }
                if (message_id != -1) {
                    $.extend(parameters, {
                        message_id: message_id,
                        message: message != '' ? message.replace(shortcode, success) : success,
                        payload: payload
                    });
                }
                SBF.ajax(parameters, (response) => {
                    if (response != false && !SBF.errorValidation(response)) {
                        switch (type) {
                            case 'email':
                                for (var key in user_settings) {
                                    activeUser().set(key, user_settings[key][0]);
                                }
                                if (active_conversation && SBChat.conversation.getLastUserMessage() !== false && CHAT_SETTINGS['notify-agent-email']) {
                                    SBChat.sendEmail(SBChat.conversation.getLastUserMessage().get('message'));
                                }
                                SBF.loginCookie(response[1]);
                                if (rich_message_id == 'sb-subscribe-form') {
                                    SBF.ajax({
                                        function: 'subscribe-email',
                                        email: activeUser().get('email')
                                    });
                                }
                                SBF.event('SBNewEmailAddress', { id: rich_message_id, name: activeUser().name, email: activeUser().get('email') });
                                break;
                            case 'registration':
                                SBF.loginCookie(response[1]);
                                if (activeUser() == false) {
                                    activeUser(new SBUser(response[0]));
                                    SBChat.initChat();
                                    SBChat.sendMessage(bot_id, success);
                                } else {
                                    for (var key in user_settings) {
                                        activeUser().set(key, user_settings[key][0]);
                                    }
                                }
                                if (SBChat.dashboard) {
                                    $(main).removeClass('sb-init-form-active');
                                    $(area).remove();
                                    if (!SBChat.isInitDashboard()) {
                                        SBChat.hideDashboard();
                                    }
                                }
                                delete this.cache['registration'];
                                SBF.event('SBRegistrationForm', { id: rich_message_id, user: user_settings, extra: payload['rich-messages'][rich_message_id]['result']['extra'] });
                                SBF.event('SBNewEmailAddress', { id: rich_message_id, name: activeUser().name, email: activeUser().get('email') });
                                break;
                        }
                        if (message_id == -1) {
                            $(element).closest('.sb-rich-message').html(success);
                        } else if ((!('type' in payload) || payload['type'] != 'close-message') && (!dialogflow_unknow_notify && dialogflow_human_takeover == false)) {
                            SBChat.setConversationStatus(2);
                        }
                        if (!['email', 'registration', 'login', 'chips'].includes(type)) {
                            SBChat.sendBotMessage(`${rich_message_id}|${type}|${dialogflow_response}`);
                        }
                        if (CHAT_SETTINGS['slack-active']) {
                            SBChat.slackMessage(activeUser().id, activeUser().name, activeUser().image, success);
                        }
                        if (SBPusher.active) SBChat.update();
                        SBF.event('SBRichMessageSubmit', { result: response, data: payload['rich-messages'][rich_message_id], id: rich_message_id });
                    } else {
                        SBForm.showErrorMessage(area, SBForm.getRegistrationErrorMessage(response, 'response'));
                        if (SBChat.dashboard) {
                            SBChat.scrollBottom();
                        }
                        $(input).sbLoading(false);
                    }
                });
            }
        },

        // Return the shortcode name and the shortcode settings
        shortcode: function (shortcode) {
            if (shortcode.indexOf(' ') < 0) {
                return [shortcode.slice(1, -1), {}];
            }
            let result = {};
            let shortcode_name = shortcode.substr(1, shortcode.indexOf(' ') - 1);
            shortcode = shortcode.slice(1, -1).substr(shortcode_name.length + 1);
            let settings = shortcode.split('" ');
            for (var i = 0; i < settings.length; i++) {
                if (settings[i].includes('=')) {
                    let item = [settings[i].substr(0, settings[i].indexOf('=')), settings[i].substr(settings[i].indexOf('=') + 2)];
                    result[$.trim(item[0])] = item[1].replace(/"/g, '');
                }
            }
            return [shortcode_name, result];
        },

        // Init the rich message inputs
        initInputs: function (code) {
            code = $.parseHTML('<div>' + code + '</div>');
            $(code).find('.sb-input input').each(function () {
                if ($(this).val() != '') {
                    $(this).siblings().addClass('sb-active sb-filled');
                }
            });
            return $(code).html();
        },

        // Timetable shortcode
        timetable: function (code) {
            let table = $.parseHTML(`<div>${code}</div>`);
            let offset = $(table).find('[data-offset]').attr('data-offset');
            offset = offset == '' ? 0 : parseInt(offset);
            $(table).find('[data-time]').each(function () {
                let times = $(this).attr('data-time').split('|');
                code = ''
                for (var i = 0; i < times.length; i++) {
                    if (times[i] == 'closed') {
                        code += sb_('Closed');
                        break;
                    } else if (times[i] != '') {
                        let hm = times[i].split(':');
                        let time = SBF.convertUTCDateToLocalDate(`01/01/2000 ${hm[0]}:${hm[1]}`, offset);
                        code += time.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) + (i == 0 || i == 2 ? `<span>${sb_('to')}</span>` : i == 1 && times[i + 1] != '' ? `<br />` : '');
                    }
                }
                $(table).find(' > div > span').html(`<i class="sb-icon-clock"></i> ${sb_('Time zone')} ${Intl.DateTimeFormat().resolvedOptions().timeZone}`);
                $(this).html(code);
            });
            return $(table).html();
        },

        // Slider
        sliderChange: function (id, direction = 'left') {
            let slider = $(chat).find(`#${id}`);
            if (slider.length && !$(slider).hasClass('sb-moving')) {
                let items = $(slider).find('.sb-slider > div > div');
                let item = $(items).eq(0);
                let width = Math.ceil($(item).closest('.sb-slider').width());
                let negative = (direction == 'right' ? -1 : 1);
                let margin = parseFloat(parseFloat(parseFloat($(item).css('margin-left')) + (width * negative)).toFixed(2)) + (-0.5 * negative);
                let check = width * (items.length - 1) * -1;
                if (margin < 1 && margin >= check) {
                    $(item).css('margin-left', margin + 'px');
                    $(slider).addClass('sb-moving');
                    setTimeout(() => { $(slider).removeClass('sb-moving'); }, 1200);
                }
                $(slider).find('.sb-icon-arrow-right').sbActivate(!(check > (margin - 15) && check < (margin + 15)));
                $(slider).find('.sb-icon-arrow-left').sbActivate(margin < -10);
            }
        }
    }

    /* 
    * ----------------------------------------------------------
    * FORM METHODS
    * ----------------------------------------------------------
    */

    var SBForm = {

        // Get all settings
        getAll: function (area) {
            let settings = {};
            $(area).find('.sb-input[id]').each((i, element) => {
                settings[$(element).attr('id')] = this.get(element);
            });
            return settings;
        },

        // Get a single setting
        get: function (input) {
            let type = $(input).data('type');
            let name = sb_(SBF.escape($(input).find(' > span').html()));
            switch (type) {
                case 'image':
                    let url = $(input).find('.image').attr('data-value');
                    return [SBF.null(url) ? '' : url, name];
                    break;
                case 'select':
                    return [SBF.escape($(input).find('select').val()), name];
                    break;
                default:
                    let target = $(input).find('input');
                    return [SBF.escape($(target).length ? $(target).val() : $(input).find('[data-value]').attr('data-value')), name];
                    break;
            }
        },

        // Set a single setting
        set: function (item, value) {
            if ($(item).length) {
                let type = $(item).data('type');
                switch (type) {
                    case 'image':
                        if (value == '') {
                            $(item).find('.image').removeAttr('data-value').removeAttr('style');
                        } else {
                            $(item).find('.image').attr('data-value', value).css('background-image', `url("${value}")`);
                        }
                        break;
                    case 'select':
                        $(item).find('select').val(value);
                        break;
                    default:
                        $(item).find('input').val(value);
                        break;
                }
                return true;
            }
            return false;
        },

        // Clear all the input values
        clear: function (area) {
            $(area).find('.sb-input').each((i, element) => {
                this.set(element, '');
            });
            this.set($(area).find('#user_type'), 'user');
        },

        // Check for errors on user input
        errors: function (area) {
            let have_errors = false;
            let items = $(area).find('[required]').removeClass('sb-error');
            $(items).each(function (i) {
                let type = $(this).attr('type');
                let value = $(this).val();
                if (value == '') {
                    have_errors = true;
                } else if (type == 'password') {
                    if (type == 'password' && (value.length < 8 || (items.length > (i + 1) && $(items[i + 1]).attr('type') == 'password' && $(items[i + 1]).val() != value))) {
                        have_errors = true;
                    }
                } else if (type == 'email' && (value.indexOf('@') < 0 || value.indexOf('.') < 0)) {
                    have_errors = true;
                }
                if (have_errors) {
                    $(this).addClass('sb-error');
                }
            });
            items = $(area).find('[data-required]').removeClass('sb-error');
            $(items).each(function () {
                if (SBF.null($(this).attr('data-value'))) {
                    $(this).addClass('sb-error');
                    have_errors = true;
                }
            });
            return have_errors;
        },

        // Display a error message
        showErrorMessage: function (area, message) {
            $(area).find('.sb-info').html(sb_(message)).sbActivate();
            clearTimeout(timeout);
            timeout = setTimeout(function () {
                $(area).find('.sb-info').sbActivate(false);
            }, 7000);
        },

        // Display a success message
        showSuccessMessage: function (area, message) {
            $(area).find('.sb-info').remove();
            $(area).addClass('sb-success').find('.sb-content').html(`<div class="sb-text">${message}</div>`);
        },

        // Return the registration error message
        getRegistrationErrorMessage(area_or_response, type = 'validation') {
            if (type == 'response') return SBF.errorValidation(area_or_response, 'duplicate-email') ? 'This email is already in use. Please use another email.' : 'Error. Please check your information and try again.'
            let name = $(area_or_response).find('#last_name').length ? 'first name, last name' : 'name';
            return $(area_or_response).find('#password').length ? `Please insert your ${name}, a valid email, and a password (8 character minimum).` : `Please insert your ${name}, and a valid email.`;
        }
    }
    window.SBForm = SBForm;

    /* 
    * ----------------------------------------------------------
    * # APPS
    * ----------------------------------------------------------
    */

    var SBApps = {

        // Get the login data 
        login: function () {
            if (this.is('wp') && typeof SB_WP_ACTIVE_USER != ND) {
                return [CHAT_SETTINGS['wp-users-system'] == 'wp' ? (typeof SB_WP_ACTIVE_USER != ND ? SB_WP_ACTIVE_USER : false) : false, 'wp'];
            }
            if (typeof SB_PERFEX_ACTIVE_USER != ND) {
                return [SB_PERFEX_ACTIVE_USER, 'perfex'];
            }
            if (typeof SB_WHMCS_ACTIVE_USER != ND) {
                return [SB_WHMCS_ACTIVE_USER, 'whmcs'];
            }
            if (typeof SB_AECOMMERCE_ACTIVE_USER != ND) {
                return [SB_AECOMMERCE_ACTIVE_USER, 'aecommerce'];
            }
            return false;
        },

        // Check if an app is installed
        is: function (name) {
            if (name == 'wordpress' || name == 'wp') return CHAT_SETTINGS['wp'];
            return CHAT_SETTINGS[name];
        },

        // WordPress
        wordpress: {

            // Ajax
            ajax: function (action, data, onSuccess) {
                $.ajax({
                    method: 'POST',
                    url: SB_WP_AJAX_URL,
                    data: $.extend({ action: 'sb_wp_ajax', type: action }, data)
                }).done((response) => {
                    if (onSuccess !== false) {
                        onSuccess(response);
                    }
                });
            }
        },

        // WooCoommerce
        woocommerce: {

            // Update the cart
            updateCart: function (action, product_id, onSuccess = false) {
                SBApps.wordpress.ajax(action, { product_id: product_id }, onSuccess);
            },

            // Waiting list
            waitingList: function (action = 'request', product_id = false) {
                if (typeof SB_WP_WAITING_LIST !== ND && (action != 'request' || SB_WP_WAITING_LIST && SBF.storageTime('waiting-list-' + SB_WP_PAGE_ID, 24)) && activeUser()) {
                    SBF.ajax({
                        function: 'woocommerce-waiting-list',
                        product_id: product_id === false ? SB_WP_PAGE_ID : product_id,
                        conversation_id: SBChat.conversation.id,
                        action: action,
                        token: SBChat.dialogflow_token
                    }, (response) => {
                        if (response) {
                            SBF.storageTime('waiting-list-' + SB_WP_PAGE_ID);
                            if (action == 'request' && (!SBChat.chat_open || SBChat.dashboard)) {
                                SBChat.updateConversations();
                            }
                        }
                    });
                }
            }
        },

        // Dialogflow
        dialogflow: {

            // Check if Dialogflow is active or deactivate it
            active: function (active = true) {
                if (active === false) {
                    CHAT_SETTINGS['dialogflow-active'] = false;
                    SBF.storageTime('dialogflow');
                    return false;
                }
                return CHAT_SETTINGS['dialogflow-active'] && !admin && SBF.storageTime('dialogflow', 1) && (!CHAT_SETTINGS['bot-office-hours'] || !CHAT_SETTINGS['office-hours']);
            },

            // Return the notification email text
            unknowEmail: function (message) {
                return SBChat.sendEmail(`${message}<br><br><span style="color:#a8a8a8;font-size: 12px;">${sb_('This message has been sent because the Dialogflow Bot does not know the answer to the user\'s question.')}</span>`);
            },

            // Trigger the welcome Intent
            welcome: function () {
                if (CHAT_SETTINGS['dialogflow-welcome']) {
                    SBF.ajax({
                        function: 'send-bot-message',
                        message: '',
                        conversation_id: SBChat.conversation.id,
                        token: SBChat.dialogflow_token,
                        event: 'Welcome'
                    });
                }
            }
        },

        aecommerce: {

            // Update the user cart
            cart: function () {
                if (SBApps.is('aecommerce') && typeof SB_AECOMMERCE_CART != ND && typeof SB_AECOMMERCE_ACTIVE_USER != ND && storage('aecommerce') != JSON.stringify(SB_AECOMMERCE_CART)) {
                    SBF.ajax({
                        function: 'aecommerce-cart',
                        cart: SB_AECOMMERCE_CART
                    }, () => {
                        storage('aecommerce', JSON.stringify(SB_AECOMMERCE_CART));
                    });
                }
            }
        }
    }

    /*
    * ----------------------------------------------------------
    * # INIT
    * ----------------------------------------------------------
    */

    $(document).ready(function () {
        main = $('.sb-admin, .sb-admin-start');
        if (main.length) {
            admin = true;
            initialize();
            return;
        }
        let url_full;
        let url;
        let scripts = document.getElementsByTagName('script');
        let checks = ['init.js', 'main.js', 'min/init.min.js', 'min/main.min.js'];
        let init = false;
        for (var i = 0; i < scripts.length; i++) {
            let source = scripts[i].src;
            if (scripts[i].id == 'sbinit') {
                url_full = source;
                init = init ? init : url_full.includes('init.');
                break;
            } else {
                for (var j = 0; j < checks.length; j++) {
                    if (source.includes('/supportboard/js/' + checks[j])) {
                        url_full = source;
                        init = init ? init : url_full.includes('init.');
                        break;
                    }
                }
            }
        }
        if (init) {
            initialize();
            return;
        }
        let parameters = SBF.getURL(false, url_full);
        if ('url' in parameters) {
            url_full = parameters['url'];
        }
        if (typeof SB_TICKETS != ND || ('mode' in parameters && parameters['mode'] == 'tickets')) {
            tickets = true;
            parameters['mode'] = 'tickets';
        }
        if (typeof SB_DISABLED != 'undefined' && SB_DISABLED) {
            return;
        }
        url = url_full.substr(0, url_full.lastIndexOf('main.js') > 0 ? (url_full.lastIndexOf('main.js') - 4) : (url_full.lastIndexOf('main.min.js') - 8));
        SBF.cors('GET', url + '/include/init.php' + ('lang' in parameters ? '?lang=' + parameters['lang'] : '') + ('mode' in parameters ? ('lang' in parameters ? '&' : '?') + 'mode=' + parameters['mode'] : ''), (response) => {
            let target = 'body';
            if (tickets && $('#sb-tickets').length) {
                target = '#sb-tickets';
            }
            $(target).append(response);
            SBF.loadResource(url + '/css/min/' + (tickets ? 'tickets' : 'main') + '.min.css');
            if (tickets) {
                SBF.loadResource(url + '/apps/tickets/tickets.js', true);
            }
            initialize();
        });
    });

    function initialize() {
        main = $('.sb-admin, .sb-chat, .sb-tickets');

        // Initalize the chat and the user
        if (main.length && typeof SB_AJAX_URL != ND) {
            chat = $(main).find('.sb-list');
            chat_editor = $(main).find('.sb-editor');
            chat_textarea = $(chat_editor).find('textarea');
            chat_scroll_area = $(main).find(admin || tickets ? '.sb-list' : '> div > .sb-scroll-area');
            chat_header = $(chat_scroll_area).find('.sb-header');
            chat_emoji = $(chat_editor).find('.sb-emoji');
            chat_status = tickets ? $(main).find('.sb-profile-agent .sb-status') : null;
            SBChat.enabledAutoExpand();
            SBChat.audio = $(main).find("#sb-audio")[0];
            SBChat.audio_out = $(main).find("#sb-audio-out")[0];

            // Check if cookies works
            SBF.cookie('sb-check', 'ok', 1, 'set');
            if (SBF.cookie('sb-check') != 'ok') {
                cookies_supported = false;
                console.warn('Support Board: cookies not available.');
            } else {
                SBF.cookie('sb-check', false, false, false);
            }

            // Compatibility code. This code will be removed in the future versions
            if (storage('login') != false) {
                SBF.loginCookie(storage('login'));
                localStorage.removeItem('login');
            }

            SBF.ajax({
                function: 'get-front-settings'
            }, (response) => {
                CHAT_SETTINGS = response;
                bot_id = CHAT_SETTINGS['bot-id'];
                dialogflow_human_takeover = CHAT_SETTINGS['dialogflow-human-takeover'];
                dialogflow_unknow_notify = CHAT_SETTINGS['bot-unknow-notify'];
                agents_online = CHAT_SETTINGS['agents-online'];
                SBPusher.active = CHAT_SETTINGS['pusher'];
                if (typeof SB_REGISTRATION_REQUIRED != 'undefined') {
                    CHAT_SETTINGS['registration-required'] = SB_REGISTRATION_REQUIRED;
                    CHAT_SETTINGS['tickets-registration-required'] = SB_REGISTRATION_REQUIRED;
                }
                if (!admin && !CHAT_SETTINGS['chat-manual-init'] && (!CHAT_SETTINGS['disable-offline'] || agents_online) && (!CHAT_SETTINGS['disable-office-hours'] || CHAT_SETTINGS['office-hours']) && (!CHAT_SETTINGS['chat-login-init'] || SBApps.login())) {
                    SBChat.initChat();
                }
                if (CHAT_SETTINGS['cron']) {
                    setTimeout(function () {
                        SBF.ajax({ function: 'cron-jobs' });
                    }, 10000);
                }
                if (!admin && CHAT_SETTINGS['push-notifications-users']) {
                    SBPusher.initServiceWorker();
                }
                SBApps.aecommerce.cart();
                SBF.event('SBReady');
            });

            $(chat_editor).on('keydown', 'textarea', function (e) {
                if (e.which == 13 && (!tickets || CHAT_SETTINGS['tickets-enter-button']) && (!admin || !e.ctrlKey)) {
                    SBChat.submit();
                    e.preventDefault;
                    return false;
                }
                if (admin && e.which == 13 && e.ctrlKey) {
                    SBChat.insertText('\n');
                }
            });
            if (!admin) {
                SBChat.audio_out.volume = 0.4;
            }
            if (typeof SB_DEFAULT_DEPARTMENT !== ND) {
                SBChat.default_department = SB_DEFAULT_DEPARTMENT;
            }
            if (typeof SB_DEFAULT_AGENT !== ND) {
                SBChat.default_agent = SB_DEFAULT_AGENT;
            }
        } else {
            CHAT_SETTINGS = [];
            SBF.event('SBReady');
        }

        // Disable real-time if browser tab not active
        document.addEventListener('visibilitychange', function () {
            SBF.visibilityChange(document.visibilityState);
        }, false);

        $(main).on('click', function () {
            if (SBChat.tab_active == false) {
                SBF.visibilityChange();
            }
        });

        // Set the global container for both admin and front
        if (admin) {
            global = main;
            main = $(main).find('.sb-conversation');
        } else {
            global = main;
        }

        // Scroll detection
        $(chat_scroll_area).on('scroll', function (e) {
            SBChat.scrollHeader();
        });

        // Show the message menu
        $(chat).on('click', '.sb-menu-btn', function () {
            let menu = $(this).parent().find('.sb-menu');
            let active = $(menu).sbActive();
            SBF.deactivateAll();
            if (!active) {
                $(menu).sbActivate();
                SBF.deselectAll();
            }
        });

        // Mobile
        if (mobile) {
            $(chat_editor).on('click', '.sb-textarea', function () {
                $(main).addClass('sb-header-hidden');
                $(this).find('textarea').focus();
                if (chat_scroll_area[0].scrollTop === (chat_scroll_area[0].scrollHeight - chat_scroll_area[0].offsetHeight)) {
                    SBChat.scrollBottom();
                    setTimeout(() => {
                        SBChat.scrollBottom();
                    }, 200);
                }
            });

            $(chat_editor).on('focusout', '.sb-textarea', function () {
                setTimeout(() => {
                    $(main).removeClass('sb-header-hidden');
                }, 300);
            });

            $(chat_editor).on('click', '.sb-submit', function () {
                $(chat_textarea).blur();
            });
        }

        // Hide the message menu
        $(chat).on('click', '.sb-menu li', function () {
            $(this).parent().sbActivate(false);
        });

        // Delete a message
        $(chat).on('click', '.sb-menu [data-value="delete"]', function () {
            let message = $(this).closest('[data-id]');
            let message_id = $(message).attr('data-id');
            if (SBChat.user_online) {
                SBF.ajax({
                    function: 'update-message',
                    message_id: message_id,
                    message: '',
                    attachments: [],
                    payload: { 'event': 'delete-message' }
                }, () => {
                    SBChat.conversation.deleteMessage(message_id);
                    $(chat).find(`[data-id="${message_id}"]`).remove();
                });
            } else {
                SBChat.deleteMessage(message_id);
            }
        });

        // Send a message
        $(chat_editor).on('click', '.sb-submit', function () {
            SBChat.submit();
        });

        // Open the chat
        $('body').on('click', '.sb-chat-btn,.sb-responsive-close-btn,#sb-open-chat,.sb-open-chat', function () {
            SBChat.open(!SBChat.chat_open);
        });

        $('.sb-chat-btn').on('click', function(){
            gtag_report_conversion_chat()
        });

        // Show the dashboard
        $(main).on('click', '.sb-dashboard-btn', function () {
            SBChat.showDashboard();
            storage('open-conversation', 0);
            force_action = false;
        });

        // Open a conversation from the dashboard
        $(main).on('click', '.sb-user-conversations li', function () {
            SBChat.openConversation($(this).attr('data-conversation-id'));
        });

        // Start a new conversation from the dashboard
        $(main).on('click', '.sb-btn-new-conversation,.sb-departments-list > div', function () {
            if (!SBF.null($(this).data('id'))) {
                SBChat.default_department = parseInt($(this).data('id'));
            }
            force_action = 'new-conversation';
            SBChat.clear();
            SBChat.hideDashboard();
        });

        // Events uploader
        $(chat_editor).on('click', '.sb-btn-attachment', function () {
            if (!SBChat.is_busy) {
                $(chat_editor).find('.sb-upload-files').val('').click();
            }
        });

        $(chat_editor).on('click', '.sb-attachments > div > i', function (e) {
            $(this).parent().remove();
            if ($(chat_textarea).val() == '' && $(chat_editor).find('.sb-attachments > div').length == 0) {
                SBChat.activateBar(false);
            }
            e.preventDefault();
            return false;
        });

        $(chat_editor).on('change', '.sb-upload-files', function (data) {
            SBChat.busy(true);
            $(this).sbUploadFiles(function (response) {
                SBChat.uploadResponse(response);
            });
            SBF.event('SBAttachments');
        });

        $(chat_editor).on('dragover', function () {
            $(this).addClass('sb-drag');
            clearTimeout(timeout);
        });

        $(chat_editor).on('dragleave', function () {
            timeout = setTimeout(() => {
                $(this).removeClass('sb-drag');
            }, 200);
        });

        $(chat_editor).on('drop', function (e) {
            let files = e.originalEvent.dataTransfer.files;
            e.preventDefault();
            e.stopPropagation();
            if (files.length > 0) {
                for (var i = 0; i < files.length; ++i) {
                    let form = new FormData();
                    form.append('file', files[i]);
                    SBF.upload(form, function (response) { SBChat.uploadResponse(response) });
                }
            }
            $(this).removeClass('sb-drag');
        });

        // Articles
        $(main).on('click', '.sb-btn-all-articles', function () {
            SBChat.showArticles();
        });

        $(main).on('click', '.sb-articles > div', function () {
            SBChat.showArticles($(this).attr('data-id'));
        });

        $(main).on('click', '.sb-dashboard-articles .sb-input-btn .sb-submit-articles', function () {
            SBChat.searchArticles($(this).parent().find('input').val(), this);
        });

        $(chat).on('click', '.sb-rich-button a', function (e) {
            let link = $(this).attr('href');
            if (link.indexOf('#') === 0) {
                if (link.indexOf('#article-') === 0) {
                    SBChat.showArticles(link.replace('#article-', ''));
                    e.preventDefault();
                    return false;
                }
            }
        });

        // Lightbox
        $(global).on('click', '.sb-lightbox-media > i', function () {
            $(global).find('.sb-lightbox-media').sbActivate(false);
            return false;
        });

        $(main).on('click', '.sb-image', function () {
            SBF.lightbox($(this).html());
        });

        $(main).on('click', '.sb-slider-images .sb-card-img', function () {
            SBF.lightbox(`<img src="${$(this).attr('data-value')}" />`);
        });

        // Event: on conversation loaded
        $(window).on('SBConversationLoaded', function () {
            if (storage('queue')) {
                SBChat.queue(storage('queue'));
            }
        });

        // Events emoji
        $(chat_editor).on('click', '.sb-btn-emoji', function () {
            SBChat.showEmoji(this);
        });

        $(chat_emoji).on('click', '.sb-emoji-list li', function (e) {
            SBChat.insertEmoji($(this).html());
            if (mobile) {
                clearTimeout(timeout);
            }
        });

        $(chat_emoji).find('.sb-emoji-list').on('touchend', function (e) {
            timeout = setTimeout(() => {
                SBChat.mouseWheelEmoji(e);
            }, 50);
        });

        $(chat_emoji).find('.sb-emoji-list').on('mousewheel DOMMouseScroll', function (e) {
            SBChat.mouseWheelEmoji(e);
        });

        $(chat_emoji).find('.sb-emoji-list').on('touchstart', function (e) {
            SBChat.emoji_options['touch'] = e.originalEvent.touches[0].clientY;
        });

        $(chat_emoji).on('click', '.sb-emoji-bar > div', function () {
            SBChat.clickEmojiBar(this);
        });

        $(chat_emoji).on('click', '.sb-select li', function () {
            SBChat.categoryEmoji($(this).data('value'));
        });

        $(chat_emoji).find('.sb-search-btn input').on('change keyup paste', function () {
            SBChat.searchEmoji($(this).val());
        });

        // Textarea
        $(chat_textarea).on('keyup', function () {
            SBChat.textareaChange(this);
        });

        // Privacy message
        $(main).on('click', '.sb-privacy .sb-approve', function () {
            storage('privacy-approved', true);
            $(this).closest('.sb-privacy').remove();
            $(main).removeClass('sb-init-form-active');
            $(chat_header).find(' > div').css({ 'opacity': 1, 'top': 0 });
            SBChat.initChat();
            if (tickets) {
                SBTickets.showPanel('new-ticket');
            } else if (!SBChat.isInitDashboard()) {
                SBChat.hideDashboard();
            }
        });

        $(main).on('click', '.sb-privacy .sb-decline', function () {
            let privacy = $(this).closest('.sb-privacy');
            $(privacy).find('.sb-text').html($(privacy).attr('data-decline'));
            $(privacy).find('.sb-decline').remove();
            SBChat.scrollBottom(true);
        });

        // Popup message
        $(main).on('click', '.sb-popup-message .sb-icon-close', function () {
            SBChat.popup(true);
        });

        // Rich messages 
        $(main).on('click', '.sb-rich-message .sb-submit,.sb-rich-message .sb-select ul li', function () {
            let message = $(this).closest('.sb-rich-message');
            if (!message[0].hasAttribute('disabled')) {
                SBRichMessages.submit(message, message.attr('data-type'), this);
            };
        });

        $(main).on('click', '.sb-rich-message .sb-input > span', function () {
            $(this).sbActivate();
            $(this).siblings().focus();
        });
        $(main).on('focus', '.sb-rich-message .sb-input input', function () {
            $(this).siblings().sbActivate();
        });

        $(main).on('click', '.sb-rich-message .sb-input input', function () {
            $(this).siblings().removeClass('sb-filled');
        });

        $(main).on('focusout', '.sb-rich-message .sb-input input', function () {
            if ($(this).val() == '') {
                $(this).siblings().sbActivate(false);
            } else {
                $(this).siblings().addClass('sb-filled sb-active');
            }
        });

        $(main).on('click', '.sb-slider-arrow', function () {
            SBRichMessages.sliderChange($(this).closest('[id]').attr('id'), $(this).hasClass('sb-icon-arrow-right') ? 'right' : 'left');
        });

        // Registration and Login
        $(main).on('click', '.sb-rich-registration .sb-login-area', function () {
            $(this).closest('.sb-rich-registration').replaceWith(SBRichMessages.generate({}, 'login', 'sb-init-form'));
            SBChat.scrollBottom(true);
        });

        $(main).on('click', '.sb-rich-login .sb-registration-area', function () {
            if (CHAT_SETTINGS['registration-link'] != '') {
                document.location = CHAT_SETTINGS['registration-link'];
            } else {
                $(this).closest('.sb-rich-login').replaceWith(SBRichMessages.generate({}, 'registration', 'sb-init-form'));
                SBChat.scrollBottom(true);
            }
        });

        $(main).on('click', '.sb-rich-login .sb-submit-login', function () {
            SBF.loginForm(this, false, (response) => {
                activeUser(new SBUser(response[0]));
                $(main).removeClass('sb-init-form-active');
                $(this).closest('.sb-rich-login').remove();
                force_action = 'open-conversation';
                SBChat.initChat();
                if (!SBChat.isInitDashboard()) {
                    SBChat.hideDashboard();
                }
            });
        });

        // Social share buttons
        $(chat).on('click', '.sb-social-buttons div', function () {
            SBF.openWindow($(this).attr('data-link'));
        });

        // WooCommerce
        $(chat).on('click', '.sb-rich-woocommerce-button a, [href="#"].sb-card-btn', function (e) {
            if (!$(this).sbLoading()) {
                let settings = SBF.settingsStringToArray($(this).closest('.sb-rich-message').attr('data-settings'));
                let checkout = settings['link-type'] == 'checkout' || settings['checkout'];
                let product_ids = $(this)[0].hasAttribute('data-ids') ? $(this).attr('data-ids').split(',') : [settings['id'].split('|')[$(this).parent().index()]];
                if (product_ids.length) {
                    $(this).sbLoading(true);
                    SBApps.wordpress.ajax('button-purchase', { product_ids: product_ids, checkout: checkout, coupon: settings['coupon'] }, (response) => {
                        if (checkout) {
                            document.location = response;
                        } else {
                            $(this).addClass('sb-icon-check').sbLoading(false);
                        }
                    });
                }
            }
            e.preventDefault();
            return false;
        });

        $(chat).on('click', '#sb-waiting-list .sb-submit', function () {
            if ($(this).index() == 0) {
                setTimeout(() => { SBApps.woocommerce.waitingList('submit') }, 1000);
            }
        });

        $(window).on('SBNewEmailAddress', function (e, response) {
            if (response['id'] == 'sb-waiting-list-email') {
                SBApps.woocommerce.waitingList('submit');
            }
        });

        /*
        * ----------------------------------------------------------
        * COMPONENTS
        * ----------------------------------------------------------
        */

        // Search
        $(global).on('click', '.sb-search-btn > i', function () {
            let parent = $(this).parent();
            let active = $(parent).sbActive();
            if (active) {
                $(parent).find('input').val('');
                setTimeout(function () {
                    $(parent).find('input').trigger('change');
                }, 550); 
            };
            $(parent).sbActivate(!active);
            $(parent).find('input').focus();
            $(global).find('.sb-select ul').sbActivate(false);
        });

        // Select
        $(global).on('click', '.sb-select', function () {
            $(this).find('ul').toggleClass('sb-active');
        });

        $(global).on('click', '.sb-select li', function () {
            let select = $(this).closest('.sb-select');
            let value = $(this).data('value');
            var item = $(select).find(`[data-value="${value}"]`);
            $(select).find('li').sbActivate(false);
            $(select).find('p').attr('data-value', value).html($(item).html());
            $(item).sbActivate();
        });

        // Image uploader
        $(global).on('click', '.sb-input-image .image', function () {
            upload_target = $(this).parent();
            $(chat_editor).find('.sb-upload-files').click();
        });

        $(global).on('click', '.sb-input-image .image > .sb-icon-close', function (e) {
            $(this).parent().removeAttr('data-value').css('background-image', '');
            e.preventDefault();
            return false;
        });
    }

}(jQuery));

Directory Contents

Dirs: 0 × Files: 2

Name Size Perms Modified Actions
0 B lrw-r--r-- 2026-07-14 05:58:09
Edit Download
200.55 KB lrw-rw-rw- 2026-07-06 10:38:36
Edit Download

If ZipArchive is unavailable, a .tar will be created (no compression).