1 2/* 3 * ========================================================== 4 * MAIN SCRIPT 5 * ========================================================== 6 * 7 * Main JavaScript file. © 2017-2026 board.support. All rights reserved. 8 * 9 */ 10 11'use strict'; 12 13(function ($) { 14 15 var version = '3.9.1'; 16 var main; 17 var global; 18 var upload_target; 19 var admin = false; 20 var tickets = false; 21 var timeout = false; 22 var timeout_typing = false; 23 var interval = false; 24 var timeout_debounce = []; 25 var previous_search; 26 var sb_current_user = false; 27 var chat; 28 var chat_editor; 29 var chat_textarea; 30 var chat_header; 31 var chat_status; 32 var chat_emoji; 33 var chat_scroll_area; 34 var chat_overlay_panel; 35 var label_date_items = false; 36 var label_date_history = [9999999, '']; 37 var label_date_timeout = [false, false]; 38 var document_title = document.title; 39 var CHAT_SETTINGS = {}; 40 var mobile = $(window).width() < 465; 41 var bot_id; 42 var force_action = ''; 43 var dialogflow_human_takeover; 44 var agents_online = false; 45 var ND = 'undefined'; 46 var cookies_supported = true; 47 var utc_offset_user = (new Date()).getTimezoneOffset() * 60000; 48 var cloud_data = false; 49 var articles_page = false; 50 var ajax_calls; 51 var audio_mp3; 52 var audio_recorder_dom; 53 var audio_recorder_dom_time; 54 var audio_recorder; 55 var audio_recorder_chunks = []; 56 var audio_recorder_time = [0, false]; 57 var audio_recorder_time_player = [0, false]; 58 var audio_recorder_stream; 59 var prevent_focusout = false; 60 var init_push_notifications = false; 61 var conversation_update_last_check = 0; 62 var is_shopify = typeof Shopify !== ND; 63 var call_timer_interval; 64 var call_bar; 65 var call_seconds = 0; 66 var call_data = false; 67 68 /* 69 * ---------------------------------------------------------- 70 * EXTERNAL SCRIPTS 71 * ---------------------------------------------------------- 72 */ 73 74 // Auto Expand Scroll Area | Schiocco 75 $.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) } }); 76 77 // Autolink-js 78 (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):\/\/[\w\-\u0080-\uFFFF+&@#\/%?=()~_|!:,.;]*[\w\-\u0080-\uFFFF+&@#\/%=~_|])/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); 79 80 /* 81 * ---------------------------------------------------------- 82 * FUNCTIONS 83 * ---------------------------------------------------------- 84 */ 85 86 var SBF = { 87 visibility_status: 'visible', 88 loop_prevention: false, 89 90 // Main Ajax function 91 ajax: function (data, onSuccess = false) { 92 if (ajax_calls) { 93 ajax_calls[0].push(data); 94 ajax_calls[1].push(onSuccess); 95 } else { 96 ajax_calls = [[data], [onSuccess]]; 97 setTimeout(() => { 98 let onSuccessCalls = ajax_calls[1]; 99 let data_auto = { 'login-cookie': SBF.loginCookie(), current_url: window.location.href } 100 if (activeUser()) { 101 data_auto.user_id = activeUser().id; 102 } 103 if (typeof SB_LANG != ND) { 104 data_auto.language = SB_LANG; 105 } 106 if (cloud_data) { 107 data_auto.cloud = cloud_data; 108 } 109 if (location.search.includes('debug')) { 110 data_auto.debug = true; 111 } 112 $.ajax({ 113 method: 'POST', 114 url: SB_AJAX_URL, 115 data: $.extend({ function: 'ajax_calls', calls: ajax_calls[0] }, data_auto) 116 }).done((response) => { 117 let result; 118 if (Array.isArray(response)) { 119 result = response; 120 }
120 else if (response === 'invalid-session') { 121 setTimeout(() => { SBF.reset() }, 1000); 122 } else if (admin && SB_ADMIN_SETTINGS.cloud && response.includes('no-credits') && !response.includes('"value":"no-credits')) { 123 return SBCloud.creditsAlertQuota(); 124 } else { 125 try { 126 result = typeof response === 'string' || response instanceof String ? JSON.parse(response) : response; 127 } catch (e) { 128 this.ajax_error(response, data); 129 return; 130 } 131 } 132 for (var i = 0; i < result.length; i++) { 133 let result_sub = result[i]; 134 if (!Array.isArray(result_sub) && !result_sub.includes('["error')) { 135 result_sub = result; 136 } 137 if (result_sub[0] == 'success') { 138 onSuccess = onSuccessCalls[i]; 139 if (onSuccess) { 140 onSuccess(result_sub[1]); 141 } 142 } else if (SBF.errorValidation(result_sub)) { 143 if (onSuccess) { 144 onSuccess(result_sub); 145 } 146 } else { 147 if (admin) { 148 if (result_sub[1] == 'security-error') { 149 setTimeout(() => { SBF.reset() }, 1000); 150 } 151 SBAdmin.conversations.busy = false; 152 } 153 SBChat.is_busy_update = false; 154 SBChat.busy(false); 155 if (result_sub[1] == 'login-data-error' && !this.loop_prevention) { 156 return; 157 } 158 let result_sub_arr = JSON.parse(result_sub); 159 if (Array.isArray(result_sub_arr) && result_sub_arr[0] == 'error' && result_sub_arr[2] && result_sub_arr[3]) { 160 SBF.error(result_sub_arr[3], result_sub_arr[2]); 161 } else { 162 SBF.error(JSON.stringify(result_sub).replace(/\\/g, "").replace(/\"/g, "").replace(/\[/g, "").replace(/\]/g, "").replace('error,', ''), data.function); 163 } 164 } 165 } 166 }).fail((jqXHR, textStatus, error) => { 167 if (error) { 168 this.ajax_error('HTTP CURL ERROR'); 169 console.log(error); 170 } 171 }); 172 ajax_calls = false; 173 }, 100); 174 } 175 }, 176 177 ajax_error: function (response, data = false) { 178 if (admin) { 179 SBAdmin.conversations.busy = false; 180 SBApps.dialogflow.smart_reply_busy = false; 181 } 182 if (SBApps.dialogflow.busy) { 183 SBApps.dialogflow.busy = false; 184 if (!admin && SBChat.conversation) { 185 SBF.ajax({ function: 'open-ai-send-fallback-message', conversation_id: SBChat.conversation.id }); 186 SBChat.typing(-1, 'stop'); 187 } 188 } 189 SBChat.is_busy_update = false; 190 SBChat.busy(false); 191 console.log(response); 192 SBF.error(response.length > 500 ? response.substr(0, 500) + '... Check the console for more details.' : response, `SBF.ajax.${data ? data.function : ''}`); 193 }, 194 195 // Cors function 196 cors: function (method = 'GET', url, onSuccess) { 197 let xhr = new XMLHttpRequest(); 198 if ('withCredentials' in xhr) { 199 xhr.open(method, url, true); 200 } else if (typeof XDomainRequest != ND) { 201 xhr = new XDomainRequest(); 202 xhr.open(method, url); 203 } else { 204 return false;
205 } 206 xhr.onload = function () { 207 onSuccess(xhr.responseText); 208 }; 209 xhr.onerror = function () { 210 return false; 211 }; 212 xhr.send(); 213 }, 214 215 // Uploads 216 upload: function (form, onSuccess) { 217 if (cloud_data) { 218 form.append('cloud', cloud_data); 219 } 220 jQuery.ajax({ 221 url: SB_URL + '/include/upload.php', 222 cache: false, 223 contentType: false, 224 processData: false, 225 data: form, 226 type: 'POST', 227 success: function (response) { 228 onSuccess(response); 229 } 230 }); 231 }, 232 233 // Get file type 234 getFileType: function (url_or_name) { 235 let file_name = String(url_or_name).split('#')[0].split('?')[0].split('/').pop(); 236 let file_extension = file_name.includes('.') ? file_name.split('.').pop().toLowerCase() : ''; 237 if (/jpg|jpeg|png|gif|webp/.test(file_extension)) { 238 return 'image'; 239 } 240 if (/mp3|ogg|wav|aac/.test(file_extension)) { 241 return 'audio'; 242 } 243 if (/mp4|mkv|vob|3gp|webm/.test(file_extension)) { 244 return 'video'; 245 } 246 return 'file'; 247 }, 248 249 // Get file icon 250 getFileIcon: function (url_or_name) { 251 let file_extension = url_or_name.split('.').pop().split('?')[0].toLowerCase(); 252 let icons = { 253 audio: ['mp3', 'wav', 'ogg', 'm4a'], 254 video: ['mp4', 'avi', 'mov', 'mkv', 'webm', 'wmv'], 255 pdf: ['pdf'], 256 doc: ['doc', 'docx', 'rtf', 'odt'], 257 ppt: ['ppt', 'pptx', 'key', 'odp'], 258 xls: ['xls', 'xlsx', 'csv', 'ods'], 259 archive: ['zip', 'rar', '7z', 'tar', 'gz', 'bz2'], 260 image: ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg', 'tiff', 'heic', 'psd', 'ai'], 261 text: ['txt', 'log', 'md', 'json'] 262 }; 263 for (let key in icons) { 264 if (icons[key].includes(file_extension)) { 265 return key; 266 } 267 } 268 return 'default'; 269 }, 270 271 // UTC Time 272 UTC: function (datetime) { 273 return new Date(datetime).getTime() - utc_offset_user; 274 }, 275 276 // Check if a variable is null or empty 277 null: function (obj) { if (typeof (obj) !== ND && obj !== null && obj !== 'null' && obj !== false && (obj.length > 0 || typeof (obj) == 'number' || typeof (obj.length) == ND) && obj !== ND) return false; else return true; }, 278 279 // Deactivate and hide the elements 280 deactivateAll: function () { 281 if (global) { 282 global.find('.sb-popup, .sb-tooltip, .sb-list .sb-menu, .sb-select ul').sbActive(false); 283 } 284 }, 285 286 // Deselect the content of the target 287 deselectAll: function () { 288 if (window.getSelection) { 289 window.getSelection().removeAllRanges(); 290 } else if (document.selection) { 291 document.selection.empty(); 292 } 293 }, 294 295 // Get URL parameters 296 getURL: function (name = false, url = false) { 297 if (!url) { 298 url = location.search; 299 } 300 if (name == false) { 301 var c = url.split('?').pop().split('&'); 302 var p = {}; 303 for (var i = 0; i < c.length; i++) { 304 var d = c[i].split('='); 305 p[d[0]] = SBF.escape(d[1]); 306 } 307 return p; 308 } 309 if (url.indexOf('?') > 0) { 310 url = url.substr(0, url.indexOf('?')); 311 } 312 return SBF.escape(decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(url) || [, ""])[1].replace(/\+/g, '%20') || "")); 313 }, 314 315 URL: function () { 316 let url = window.location.href; 317 return url.includes('?') ? url.substring(0, url.indexOf('?')) : url; 318 }, 319 320 // Convert a string to slug and inverse
321 stringToSlug: function (string) { 322 let map = { 323 'à¸': 'k', 'à¸': 'kh', 'à¸': 'kh', 'à¸': 'kh', 'ภ': 'kh', 'à¸': 'kh', 'à¸': 'ng', 324 'à¸': 'ch', 'à¸': 'ch', 'à¸': 'ch', 'à¸': 's', 'à¸': 'ch', 'à¸': 'y', 'à¸': 'd', 325 'à¸': 't', 'à¸': 'th', 'à¸': 'th', 'à¸': 'th', 'à¸': 'n', 'à¸': 'd', 'à¸': 't', 326 'à¸': 'th', 'à¸': 'th', 'à¸': 'th', 'à¸': 'n', 'à¸': 'b', 'à¸': 'p', 'à¸': 'ph', 327 'à¸': 'f', 'à¸': 'ph', 'à¸': 'f', 'ภ': 'ph', 'ม': 'm', 'ย': 'y', 'ร': 'r', 328 'ล': 'l', 'ว': 'w', 'ศ': 's', 'ษ': 's', 'ส': 's', 'ห': 'h', 'ฬ': 'l', 329 'à¸': 'o', 'ฮ': 'h', 'ะ': 'a', 'ั': 'a', 'า': 'a', 'ำ': 'am', 'ิ': 'i', 330 'ี': 'i', 'ึ': 'ue', 'ื': 'ue', 'ุ': 'u', 'ู': 'u', 'à¹': 'e', 'à¹': 'ae', 331 'à¹': 'o', 'à¹': 'ai', 'à¹': 'ai', 'ا': 'a', 'ب': 'b', 'ت': 't', 'Ø«': 'th', 'ج': 'j', 'Ø': 'h', 'Ø®': 'kh', 332 'د': 'd', 'ذ': 'dh', 'ر': 'r', 'ز': 'z', 'س': 's', 'Ø´': 'sh', 'ص': 's', 333 'ض': 'd', 'Ø·': 't', 'ظ': 'z', 'ع': 'a', 'غ': 'gh', 'Ù': 'f', 'Ù': 'q', 334 'Ù': 'k', 'Ù': 'l', 'Ù ': 'm', 'Ù': 'n', 'Ù': 'h', 'Ù': 'w', 'Ù': 'y', 335 'ä½ ': 'ni', '好': 'hao', 'ä¸': 'shi', 'ç': 'jie', 'æ': 'wo', 'æ¯': 'shi', 336 'ä¸': 'zhong', 'å½': 'guo', '人': 'ren', 'è°¢': 'xie', 'å': 'zai', 'è§': 'jian' 337 }; 338 let from = "åà áãäâèéëêìÃïîòóöôùúüûñç·/_,:;"; 339 let to = "aaaaaaeeeeiiiioooouuuunc------"; 340 string = string.trim().toLowerCase().split('').map(char => map[char] || char).join(''); 341 for (var i = 0, l = from.length; i < l; i++) { 342 string = string.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i)); 343 } 344 return string.replace(/[^a-z0-9 -]/g, '').replace(/\s+/g, '-').replace(/-+/g, '-').replace(/^-+/, '').replace(/-+$/, '').replace(/ /g, ''); 345 }, 346 347 slugToString: function (string) { 348 string = string.replace(/_/g, ' ').replace(/-/g, ' '); 349 return string.charAt(0).toUpperCase() + string.slice(1); 350 }, 351 352 // Random string 353 random: function () { 354 let chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; 355 let result = ''; 356 for (var i = 5; i > 0; --i) result += chars[Math.floor(Math.random() * 62)]; 357 return result; 358 }, 359 360 // Check if a user type is an agent 361 isAgent: function (user_type) { 362 return user_type == 'agent' || user_type == 'admin' || user_type == 'bot'; 363 }, 364 365 // Beautify the time 366 beautifyTime: function (datetime, extended = false, future = false) { 367 let date; 368 if (datetime == '0000-00-00 00:00:00') { 369 return ''; 370 } 371 if (datetime.indexOf('-') > 0) { 372 let arr = datetime.split(/[- :]/); 373 date = new Date(arr[0], arr[1] - 1, arr[2], arr[3], arr[4], arr[5]); 374 } else { 375 let arr = datetime.split(/[. :]/); 376 date = new Date(arr[2], arr[1] - 1, arr[0], arr[3], arr[4], arr[5]); 377 } 378 let now = new Date(); 379 let date_string = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds())); 380 let diff_days = ((now - date_string) / 86400000) * (future ? -1 : 1); 381 let days = [sb_('Sunday'), sb_('Monday'), sb_('Tuesday'), sb_('Wednesday'), sb_('Thursday'), sb_('Friday'), sb_('Saturday')]; 382 let time = date_string.toLocaleTimeString(navigator.language, { hour: '2-digit', minute: '2-digit' }); 383 if (time.charAt(0) === '0' && (time.includes('PM') || time.includes('AM'))) { 384 time = time.substring(1); 385 } 386 if (diff_days < 1 && now.getDate() == date_string.getDate()) { 387 return extended ? `<span>${sb_('Today')}</span> <span>${time}</span>` : `<span data-today>${time}</span>`;
388 } else if (diff_days < 6) { 389 return `<span>${days[date_string.getDay()]}</span>${extended ? ` <span>${time}</span>` : ''}`; 390 } else { 391 return `<span>${date_string.toLocaleDateString(undefined, { year: '2-digit', month: '2-digit', day: '2-digit' })}</span>${extended ? ` <span>${time}</span>` : ''}`; 392 } 393 }, 394 395 // Get the unix timestamp value of a date string with format yyyy-mm-dd hh:mm:ss 396 unix: function (datetime) { 397 let arr = datetime.split(/[- :]/); 398 return Date.UTC(arr[0], arr[1] - 1, arr[2], arr[3], arr[4], arr[5]); 399 }, 400 401 // Generate a string containing the agent location and time 402 getLocationTimeString: function (details, onSuccess) { 403 if (details.timezone) { 404 let location = {}; 405 location.timezone = details.timezone.value; 406 location.country = details.country ? details.country.value : location.timezone.split('/')[0].replace(/_/g, ' '); 407 location.city = details.city ? details.city.value : location.timezone.split('/')[1].replace(/_/g, ' '); 408 onSuccess(`${new Intl.DateTimeFormat(undefined, { timeZone: location.timezone, hour: '2-digit', minute: '2-digit' }).format(new Date())} ${sb_('in')} ${location.city ? location.city : ''}${location.country ? ', ' + location.country : ''}`); 409 } 410 }, 411 412 // Date string 413 dateDB: function (date) { 414 if (date == 'now') { 415 date = (new Date).toISOString().replace('T', ' '); 416 if (date.indexOf('.') > 0) { 417 date = date.substr(0, date.indexOf('.')); 418 } 419 return date; 420 } else { 421 return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`; 422 } 423 }, 424 425 // Convert seconds to second and minutes 426 getMinutesSeconds: function (seconds) { 427 let minutes = Math.floor(seconds / 60); 428 seconds = seconds - minutes * 60; 429 return (minutes ? minutes : '0') + ':' + (seconds < 10 ? '0' + seconds : seconds); 430 }, 431 432 // Convert a date to local time 433 convertUTCDateToLocalDate: function (datetime, utc_offset = 0) { 434 datetime = new Date(datetime); // Y/m/d H:i:s 435 datetime = new Date(datetime.getTime() + utc_offset * 3600000); 436 return new Date(datetime.getTime() + utc_offset_user * -1); 437 }, 438 439 // Calls 440 call: { 441 rtc: false, 442 recorder: false, 443 mixed_stream: false, 444 local_stream: false, 445 446 answer: function (conversation_id = false) { 447 SBApps.whatsapp.call.answer(conversation_id ? conversation_id : SBChat.conversation.id, (response) => { 448 call_bar.find('#sb-call-answer').sbLoading(false); 449 if (response.success) { 450 this.startCall(); 451 } else { 452 SBF.error(JSON.stringify(response), 'SBConversations.call.answer'); 453 } 454 }); 455 }, 456 457 decline: function (is_terminate = false, conversation_id = false, onSuccess = false) { 458 SBApps.whatsapp.call.decline(conversation_id ? conversation_id : SBChat.conversation.id, is_terminate, (response) => { 459 call_bar.find('#sb-call-decline').sbLoading(false); 460 call_bar.sbActive(false); 461 this.end(); 462 if (response.success) { 463 clearInterval(call_timer_interval); 464 call_bar.removeClass('sb-call-active'); 465 } else { 466 SBF.error(JSON.stringify(response), 'SBConversations.call.decline'); 467 } 468 if (onSuccess) { 469 onSuccess(response); 470 } 471 }); 472 }, 473 474 startCall: function () { 475 call_seconds = 0; 476 clearInterval(call_timer_interval); 477 call_bar.addClass('sb-call-active'); 478 call_timer_interval = setInterval(() => { 479 call_seconds++; 480 call_bar.find('#call-timer').html(SBF.getMinutesSeconds(call_seconds)); 481 }, 1000); 482 storage('call-active', [SBChat.conversation.id, admin ? SBAdmin.conversations.active_calls[SBChat.conversation.id] : call_data]); 483 }, 484 485 start: function (channel, user_id, onSuccess) { 486 this.initRTC(() => { 487 if (channel == 'whatsapp') { 488 SBApps.whatsapp.call.start(this.rtc.localDescription.sdp, user_id, (response) => { 489 if (onSuccess) { 490 onSuccess(response); 491 } 492 }); 493 } 494 }); 495 }, 496 497 initRTC: function (onSuccess, sdp = false) { 498 (async () => { 499 const chunks = []; 500 this.rtc = new RTCPeerConnection(sdp ? {}
500 : { iceServers: [] }); 501 this.audio_ctx = new AudioContext(); 502 this.destination = this.audio_ctx.createMediaStreamDestination(); 503 this.mixed_stream = this.destination.stream; 504 this.recorder = new MediaRecorder(this.mixed_stream, { mimeType: 'audio/webm;codecs=opus' }); 505 this.recorder.ondataavailable = e => { 506 if (e.data.size) { 507 chunks.push(e.data); 508 } 509 } 510 this.recorder.onstop = () => { 511 const form = new FormData(); 512 const blob = new Blob(chunks, { type: this.recorder.mimeType }); 513 if (call_bar.hasClass('sb-call-ringing')) { 514 return; 515 } 516 form.append('file', new File([blob], `call_${SBChat.conversation.id}_${Date.now()}.webm`)); 517 const audio = new Audio(); 518 audio.src = URL.createObjectURL(blob); 519 audio.addEventListener('loadedmetadata', () => { 520 SBF.upload(form, (response) => { 521 response = JSON.parse(response); 522 if (response[0] === 'success') { 523 setTimeout(() => { 524 SBF.ajax({ 525 function: 'whatsapp-call-save-audio', 526 conversation_id: SBChat.conversation.id, 527 audio_file_url: response[1], 528 duration: audio.duration 529 }); 530 }, 1000); 531 } else { 532 SBF.error(response[1], 'call.initRTC'); 533 } 534 URL.revokeObjectURL(audio.src); 535 }); 536 }); 537 } 538 this.rtc.ontrack = (event) => { 539 const remoteStream = event.streams[0]; 540 const audioEl = global.find('#sb-audio-call')[0]; 541 audioEl.srcObject = remoteStream; 542 audioEl.autoplay = true; 543 const remoteSource = this.audio_ctx.createMediaStreamSource(remoteStream); 544 remoteSource.connect(this.destination); 545 } 546 this.local_stream = await navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true, noiseSuppression: true, autoGainControl: true } }); 547 this.audio_ctx.createMediaStreamSource(this.local_stream).connect(this.destination); 548 this.local_stream.getTracks().forEach(track => { 549 this.rtc.addTrack(track, this.local_stream); 550 }); 551 if (sdp) { 552 await this.rtc.setRemoteDescription({ type: 'offer', sdp }); 553 } 554 const local_desc = sdp ? await this.rtc.createAnswer() : await this.rtc.createOffer({ offerToReceiveAudio: true }); 555 await this.rtc.setLocalDescription(local_desc); 556 if (!sdp) { 557 this.rtc.onicecandidate = () => { }; 558 await new Promise(r => setTimeout(r, 500)); 559 } 560 if (this.audio_ctx.state !== 'running') { 561 await this.audio_ctx.resume(); 562 } 563 this.recorder.start(); 564 this.stopRecording = () => { 565 if (this.recorder && this.recorder.state !== 'inactive') { 566 this.recorder.stop(); 567 } 568 } 569 onSuccess(local_desc); 570 })(); 571 }, 572 573 show: function (show = true, is_ringing = false) { 574 call_bar.removeClass('sb-call-active sb-call-ringing'); 575 if (is_ringing) { 576 call_bar.addClass('sb-call-ringing'); 577 } 578 setTimeout(() => { 579 call_bar.sbActive(show); 580 }, 300); 581 }, 582 583 end: function () { 584 if (this.recorder && this.recorder.state !== 'inactive') { 585 this.recorder.stop(); 586 } 587 if (this.local_stream) {
588 this.local_stream.getTracks().forEach(t => t.stop()); 589 this.local_stream = null; 590 } 591 if (this.mixed_stream) { 592 this.mixed_stream.getTracks().forEach(t => t.stop()); 593 this.mixed_stream = null; 594 } 595 if (this.rtc) { 596 this.rtc.getSenders().forEach(s => s.track && s.track.stop()); 597 this.rtc.getReceivers().forEach(r => r.track && r.track.stop()); 598 this.rtc.close(); 599 this.rtc = null; 600 } 601 const audio = global.find('#sb-audio-call')[0]; 602 if (audio) { 603 audio.srcObject = null; 604 audio.pause(); 605 } 606 }, 607 608 mute: function (mute = true) { 609 if (this.local_stream) { 610 this.local_stream.getAudioTracks().forEach(track => { 611 track.enabled = !mute; 612 }); 613 } 614 } 615 }, 616 617 // Set and get users last activity 618 updateUsersActivity: function (user_id, return_user_id, onSuccess) { 619 if (SBPusher.active) { 620 onSuccess((admin && SB_ADMIN_SETTINGS.bot_id == return_user_id) || (!admin && CHAT_SETTINGS.bot_id == return_user_id) ? 'online' : (SBPusher.online_ids.includes(return_user_id) ? 'online' : 'offline')); 621 } else { 622 SBF.ajax({ 623 function: 'update-users-last-activity', 624 user_id: user_id, 625 return_user_id: return_user_id, 626 check_slack: !admin && CHAT_SETTINGS.slack_active 627 }, (response) => { 628 if (response === 'online') { 629 onSuccess('online'); 630 } else { 631 onSuccess('offline'); 632 } 633 }); 634 } 635 }, 636 637 // Search functions 638 search: function (search, searchFunction) { 639 search = search.toLowerCase(); 640 if (search == previous_search) { 641 global.find('.sb-search-btn i').sbLoading(false); 642 return; 643 } 644 clearTimeout(timeout); 645 timeout = setTimeout(function () { 646 previous_search = search; 647 searchFunction(); 648 }, 1000); 649 }, 650 651 searchClear: function (icon, onSuccess) { 652 let search = $(icon).next().val(); 653 if (search) { 654 $(icon).next().val(''); 655 onSuccess(); 656 } 657 }, 658 659 // Support Board error JS reporting 660 error: function (message, function_name) { 661 let is_full_error = message.includes(function_name); 662 if (admin && SBAdmin.is_logout) { 663 return; 664 } 665 if (message instanceof Error) { 666 message = message.message; 667 } 668 if (message[message.length - 1] == '.') { 669 message = message.slice(0, -1); 670 } 671 if (admin) { 672 if (message && !function_name.includes('update-users-last-activity') && !function_name.startsWith('security-error')) { 673 SBAdmin.infoPanel(`<pre>${message.includes(']:') ? 'Error: ' + message.substr(message.indexOf(']:') + 2) : message}. Check the console for more details.</pre>`, 'info', false, 'error', '', true); 674 global.find('.sb-lightbox-overlay').sbActive(false); 675 } 676 SBApps.dialogflow.smart_reply_busy = false; 677 } 678 global.find('.sb-loading').sbLoading(false); 679 SBChat.busy(false); 680 SBF.event('SBError', { message: message, function_name: function_name }); 681 throw new Error(is_full_error ? message : `Support Board Error [${function_name}]: ${message}.`); 682 }, 683 684 errorValidation: function (response, code = true) { 685 return Array.isArray(response) && response[0] === 'validation-error' && (code === true || response[1] == code); 686 }, 687 688 // Login 689 loginForm: function (button, area = false, onSuccess = false, isRecursion = true) { 690 button = $(button); 691 if (!button.sbLoading()) { 692 if (area === false) { 693 area = button.closest('.sb-rich-login'); 694 } else { 695 area = $(area); 696 } 697 let email = $.trim(area.find('#email input').val()); 698 let password = $.trim(area.find('#password input').val()); 699 if (!email || !password) { 700 area.find('.sb-info').html(sb_('Please insert email and password.')).sbActive(true); 701 SBChat.scrollBottom(); 702 } else { 703 SBF.ajax({ 704 function: 'login', 705 email: email, 706 password: password 707 }, (response) => { 708 if (response && Array.isArray(response)) { 709 if (!admin && this.isAgent(response[0].user_t
709ype)) { 710 SBForm.showErrorMessage(area, 'You cannot sign in as an agent.'); 711 SBChat.scrollBottom(); 712 } else { 713 let user = new SBUser(response[0]); 714 user.set('conversation_id', SBChat.conversation ? SBChat.conversation.id : false); 715 this.loginCookie(response[1]); 716 this.event('SBLoginForm', user); 717 if (onSuccess) { 718 onSuccess(response); 719 } 720 } 721 if (SBF.setting('wp-users-system') == 'wp') { 722 SBApps.wordpress.ajax('wp-login', { user: email, password: password }); 723 } 724 } else if (admin && SBApps.is('wordpress')) { 725 return SBApps.wordpress.ajax('wp-login-admin', { user: email, password: password }, () => { 726 button.sbLoading(false); 727 if (isRecursion) { 728 this.loginForm(button, area, onSuccess, false); 729 } else { 730 area.find('.sb-info').html(sb_(response === 'ip-ban' ? 'Too many login attempts. Please retry again in a few hours.' : 'Invalid email or password.')).sbActive(true); 731 } 732 }); 733 } else { 734 area.find('.sb-info').html(sb_(response === 'ip-ban' ? 'Too many login attempts. Please retry again in a few hours.' : 'Invalid email or password.')).sbActive(true); 735 if (!admin) { 736 SBChat.scrollBottom(); 737 } 738 } 739 button.sbLoading(false); 740 }); 741 area.find('.sb-info').html('').sbActive(false); 742 button.sbLoading(true); 743 } 744 } 745 }, 746 747 // Set the login cookie 748 loginCookie: function (value = false) { 749 if (value === false) { 750 return this.cookie('sb-login') ? this.cookie('sb-login') : storage('login'); 751 } 752 if (CHAT_SETTINGS.cloud) { 753 storage('login', value); 754 } else { 755 this.cookie('sb-login', value, 3650, 'set'); 756 } 757 }, 758 759 // Login 760 login: function (email = '', password = '', user_id = '', token = '', onSuccess = false) { 761 SBF.ajax({ 762 function: 'login', 763 email: email, 764 password: password, 765 user_id: user_id, 766 token: token 767 }, (response) => { 768 if (response != false && Array.isArray(response)) { 769 this.loginCookie(response[1]); 770 if (onSuccess) { 771 onSuccess(response); 772 } 773 return true; 774 } else { 775 return false; 776 } 777 }); 778 }, 779 780 // Logout 781 logout: function (reload = true) { 782 SBChat.stopRealTime(); 783 this.cookie('sb-login', '', '', false); 784 this.cookie('sb-cloud', '', '', false); 785 storage('open-conversation', ''); 786 storage('login', ''); 787 SBChat.conversations = false; 788 activeUser(false); 789 if (typeof sb_beams_client !== ND) { 790 sb_beams_client.stop(); 791 } 792 if (typeof SB_AJAX_URL !== ND) { 793 SBF.ajax({ 794 function: 'logout' 795 }, () => { 796 SBF.event('SBLogout'); 797 if (reload) { 798 setTimeout(() => { location.reload() }, 500); 799 } 800 }); 801 } 802 }, 803 804 // Return the active user 805 activeUser: function () { 806 return activeUser(); 807 }, 808 809 // Get the active user 810 getActiveUser: function (database = false, onSuccess) { 811 let app_login = SBApps.login(); 812 let token = SBF.getURL('token'); 813 if (!app_login && (storage('wp-login') || storage('whmcs-login') || storage('perfex-login') || storage('aecommerce-login'))) { 814 this.cookie('sb-login', '', '', 'delete'); 815 activeUser(false); 816 storage('login', ''); 817 storage('wp-login', ''); 818 storage('whmcs-login', ''); 819 storage('perfex-login', ''); 820 storage('aecommerce-login', ''); 821 } 822 if (token) { 823 history.replaceState(null, '', SBF.URL()); 824 } 825 SBF.ajax({ 826 function: 'get-active-user', 827 db: database, 828 login_app: JSON.stringify(app_login), 829 user_token: token 830 }, (response) => { 831 if (!response) { 832 onSuccess(); 833 return false;
834 } else { 835 if (response.cookie) { 836 SBF.loginCookie(response.cookie); 837 } 838 if (response.user_type) { 839 if (!admin && SBF.isAgent(response.user_type)) { 840 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.'; 841 if (!storage('double-login-alert')) { 842 storage('double-login-alert', true); 843 alert(message); 844 } 845 console.warn('Support Board: ' + message); 846 SBF.event('SBDoubleLoginError'); 847 } else { 848 activeUser(new SBUser(response, response.phone ? { phone: response.phone } : {})); 849 SBPusher.start(); 850 if (app_login) { 851 storage(app_login[1] + '-login', true); 852 } 853 onSuccess(); 854 SBF.event('SBActiveUserLoaded', response); 855 } 856 } 857 } 858 }); 859 }, 860 861 // Clean 862 reset: function () { 863 let cookies = ['sb-login', 'sb-cloud']; 864 for (var i = 0; i < cookies.length; i++) { 865 this.cookie(cookies[i], '', 0, false); 866 } 867 try { localStorage.removeItem('support-board') } catch (e) { } 868 this.logout(); 869 }, 870 871 // Lightbox 872 lightbox: function (content) { 873 let lightbox = $(admin ? global : main).find('.sb-lightbox-media'); 874 lightbox.sbActive(true).find(' > div').html(content); 875 if (admin) { 876 SBAdmin.open_popup = lightbox; 877 } 878 }, 879 880 // Manage the local storage 881 storage: function (key, value = ND) { 882 try { if (typeof localStorage == ND) return false } catch (e) { return false } 883 let settings = localStorage.getItem('support-board'); 884 if (settings === null) { 885 settings = {}; 886 } else { 887 settings = JSON.parse(settings); 888 } 889 if (value === ND) { 890 return key in settings ? settings[key] : false; 891 } else { 892 if (!value) { 893 delete settings[key]; 894 } else { 895 settings[key] = value; 896 } 897 localStorage.setItem('support-board', JSON.stringify(settings)); 898 } 899 }, 900 901 // Save the current time or check if the saved time is older than the given hours 902 storageTime: function (key, hours = false) { 903 let today = new Date(); 904 if (hours === false) { 905 storage(key, today.getTime()); 906 } else { 907 if (storage(key) == false) { 908 return true; 909 } 910 if ((today.getTime() - storage(key)) > (3600000 * hours)) { 911 storage(key, false); 912 return true; 913 } 914 return false; 915 } 916 }, 917 918 // Set or get a cookie 919 cookie: function (name, value = false, expiration_days = false, action = 'get', seconds = false) { 920 let cookie_https = location.protocol == 'https:' ? 'SameSite=None;Secure;' : ''; 921 let settings = window[admin ? 'SB_ADMIN_SETTINGS' : 'CHAT_SETTINGS']; 922 let domain = settings && settings.cookie_domain ? 'domain=' + settings.cookie_domain + ';' : ''; 923 if (action == 'get') { 924 if (!cookies_supported) { 925 return this.storage(name); 926 } 927 let cookies = document.cookie.split(';'); 928 for (var i = 0; i < cookies.length; i++) { 929 var cookie = cookies[i]; 930 while (cookie.charAt(0) == ' ') { 931 cookie = cookie.substring(1); 932 } 933 if (cookie.indexOf(name) == 0) { 934 let value = cookie.substring(name.length + 1, cookie.length); 935 return this.null(value) ? false : value; 936 } 937 } 938 return false;
939 } else if (action == 'set') { 940 if (!cookies_supported) { 941 this.storage(name, value); 942 } else { 943 let date = new Date(); 944 date.setTime(date.getTime() + (expiration_days * (seconds ? 1 : 86400) * 1000)); 945 document.cookie = name + "=" + value + ";expires=" + date.toUTCString() + ";path=/;" + cookie_https + domain; 946 } 947 } else if (this.cookie(name)) { 948 if (!cookies_supported) { 949 this.storage(name, ''); 950 } else { 951 document.cookie = name + "=" + value + ";expires=Thu, 01 Jan 1970 00:00:01 GMT;path=/;" + cookie_https + domain; 952 } 953 } 954 }, 955 956 // Return a front setting or set it 957 setting: function (key, value = -1) { 958 if (value !== -1) { 959 if (typeof CHAT_SETTINGS !== ND) CHAT_SETTINGS[key] = value; 960 } else return typeof CHAT_SETTINGS !== ND && key in CHAT_SETTINGS ? CHAT_SETTINGS[key] : false; 961 }, 962 963 // Return the shortcode array 964 shortcode: function (shortcode) { 965 return SBRichMessages.shortcode(shortcode); 966 }, 967 968 // Events and webhooks 969 event: function (name, parameters) { 970 $(document).trigger(name, parameters); 971 let webhooks = admin ? (typeof SB_ADMIN_SETTINGS === ND ? false : SB_ADMIN_SETTINGS.webhooks) : CHAT_SETTINGS.webhooks; 972 let webhooks_list = { SBGetUser: 'get-user', SBSMSSent: 'sms-sent', SBLoginForm: 'login', SBRegistrationForm: 'registration', SBUserDeleted: 'user-deleted', SBNewMessagesReceived: 'new-messages', SBNewConversationReceived: 'new-conversation', SBSlackMessageSent: 'slack-message-sent', SBMessageDeleted: 'message-deleted', SBRichMessageSubmit: 'rich-message', SBNewEmailAddress: 'new-email-address' }; 973 if (webhooks && name in webhooks_list) { 974 if (webhooks !== true) { 975 if (!Array.isArray(webhooks)) { 976 webhooks = webhooks.replace(/ /g, '').split(','); 977 } 978 if (!webhooks.includes(webhooks_list[name])) { 979 return; 980 } 981 } 982 SBF.ajax({ 983 function: 'webhooks', 984 function_name: name, 985 parameters: parameters 986 }); 987 } 988 }, 989 990 // Translate a string 991 translate: function (string) { 992 if ((!admin && SBF.null(CHAT_SETTINGS)) || (admin && typeof SB_TRANSLATIONS === ND)) { 993 return string; 994 } 995 let translations = admin ? SB_TRANSLATIONS : CHAT_SETTINGS.translations; 996 if (translations && translations[string]) { 997 return translations[string] ? translations[string] : string; 998 } else { 999 return string; 1000 } 1001 }, 1002 1003 // Escape a string 1004 escape: function (string) { 1005 return string ? string.replace(/</ig, '<').replace(/javascript:|onclick|onerror|ontoggle|onmouseover|onload|oncontextmenu|ondblclick|onmousedown|onmouseenter|onmouseleave|onmousemove|onmouseout|onmouseup/ig, '') : ''; 1006 }, 1007 1008 // Remove the Support Board syntax from a string 1009 strip: function (message) { 1010 message = message.replace('```', ''); 1011 let patterns = [/\*([^\**]+)\*/, /\__([^\____]+)\__/, /\~([^\~~]+)\~/, /\`([^\``]+)\`/]; 1012 for (var i = 0; i < 2; i++) { 1013 patterns.forEach(pattern => { 1014 message = message.replace(pattern, (match) => match.replace(/[\*\_\~\`]/g, '')); 1015 }); 1016 } 1017 return message.replace(/\\,/g, ',').replace(/\\:/g, ':');; 1018 }, 1019 1020 // Visibility change function 1021 visibilityChange: function (visibility = '') { 1022 this.visibility_status = visibility; 1023 let is_admin = admin && typeof SBAdmin !== ND; 1024 if (visibility == 'hidden') { 1025 if (!admin) { 1026 SBChat.stopRealTime(); 1027 } 1028 SBChat.tab_active = false;
1029 this.visibility_was_hidden = true; 1030 } else { 1031 if (activeUser() && !admin) { 1032 SBChat.startRealTime(); 1033 } 1034 SBChat.tab_active = true; 1035 clearInterval(interval); 1036 clearInterval(SBChat.audio_interval); 1037 if (SBChat.conversation) { 1038 SBChat.conversation.updateMessagesStatus(); 1039 if (SBChat.chat_open || admin) { 1040 SBChat.updateNotifications(SBChat.conversation.id); 1041 } 1042 if (is_admin) { 1043 setTimeout(() => { 1044 SBAdmin.conversations.notificationsCounterReset(SBChat.conversation.id); 1045 }, 2000); 1046 } 1047 } 1048 if (is_admin) { 1049 if ((Date.now() - (mobile ? 60000 : 180000)) > conversation_update_last_check) { 1050 SBAdmin.conversations.update(); 1051 conversation_update_last_check = Date.now(); 1052 } 1053 if (mobile) { 1054 SBChat.update(); 1055 } 1056 } 1057 document.title = document_title; 1058 this.serviceWorker.closeNotifications(); 1059 } 1060 }, 1061 1062 // Convert a settings string to an Array 1063 settingsStringToArray: function (string) { 1064 if (this.null(string)) { 1065 return []; 1066 } 1067 let result = []; 1068 string = string.split(','); 1069 for (var i = 0; i < string.length; i++) { 1070 let values = string[i].split(':'); 1071 result[values[0]] = values[1] == 'false' ? false : values[1] == 'true' ? true : values[1]; 1072 } 1073 return result; 1074 }, 1075 1076 // Open a browser window 1077 openWindow: function (link, width = 550, height = 350) { 1078 let left = (screen.width / 2) - (width / 2); 1079 let top = (screen.height / 2) - (height / 2); 1080 window.open(link, 'targetWindow', 'toolbar=no,location=no,status=no,menubar=no,scrollbars=yes,resizable=yes,width=' + width + ',height=' + height + ', top=' + top + ', left=' + left); 1081 return false; 1082 }, 1083 1084 // Load a JS or CSS file 1085 loadResource: function (src, is_js = false, onLoad = false, content = false) { 1086 let resource = document.createElement(is_js ? 'script' : 'link'); 1087 if (src) { 1088 let id = 'sb-' + SBF.stringToSlug(src.split('/').pop()); 1089 if (document.getElementById(id)) { 1090 if (onLoad) { 1091 onLoad(); 1092 } 1093 return; 1094 } 1095 if (is_js) { 1096 resource.src = src; 1097 } else { 1098 resource.href = src; 1099 } 1100 resource.type = is_js ? 'text/javascript' : 'text/css'; 1101 resource.id = id; 1102 } else { 1103 resource.innerHTML = content; 1104 } 1105 if (onLoad) { 1106 resource.onload = function () { 1107 onLoad(); 1108 } 1109 } 1110 if (!is_js) { 1111 resource.rel = 'stylesheet'; 1112 } 1113 document.head.appendChild(resource); 1114 }, 1115 1116 // Debounce 1117 debounce: function (bounceFunction, id, interval = 500) { 1118 if (!(id in timeout_debounce)) { 1119 timeout_debounce[id] = true; 1120 bounceFunction(); 1121 setTimeout(() => { 1122 delete timeout_debounce[id]; 1123 }, interval); 1124 } 1125 }, 1126 1127 // Push Notifications 1128 serviceWorker: { 1129 sw: false, 1130 timeout: false, 1131 1132 init: function () { 1133 if (navigator.serviceWorker) { 1134 navigator.serviceWorker.register(admin || typeof SB_CLOUD_SW != 'undefined' ? SB_URL.replace('/script', '') + '/sw.js?v=' + version : CHAT_SETTINGS.push_notifications_url + '?v=' + version).then((registration) => { 1135 registration.update(); 1136 this.sw = registration; 1137 }).catch(function (error) { 1138 console.warn(error); 1139 }); 1140 } 1141 }, 1142 1143 initPushNotifications: function () { 1144 if ((admin && (SB_ADMIN_SETTINGS.push_notifications_provider == 'pusher' || SB_ADMIN_SETTINGS.push_notifications_provider != 'one
1144signal')) || (!admin && (CHAT_SETTINGS.push_notifications_provider == 'pusher' || CHAT_SETTINGS.push_notifications_provider != 'pushalert'))) { // Deprecated: remove || CHAT_SETTINGS.push_notifications_provider != '' and || SB_ADMIN_SETTINGS.push_notifications_provider != 'pushalert' 1145 SBPusher.initPushNotifications(); 1146 } else { 1147 $.getScript('https://cdn.onesignal.com/sdks/web/v16/OneSignalSDK.page.js', () => { 1148 window.OneSignalDeferred = window.OneSignalDeferred || []; 1149 OneSignalDeferred.push((OneSignal) => { 1150 OneSignal.init({ 1151 appId: SB_ADMIN_SETTINGS.push_notifications_id 1152 }); 1153 }); 1154 OneSignalDeferred.push((OneSignal) => { 1155 OneSignal.User.PushSubscription.addEventListener('change', (event) => { 1156 if (event.current.optedIn) { 1157 let external_id = (admin && SB_ADMIN_SETTINGS.cloud ? SB_ADMIN_SETTINGS.cloud.cloud_user_id + '-' : (!admin && CHAT_SETTINGS.cloud ? CHAT_SETTINGS.cloud.cloud_user_id + '-' : '')) + (admin ? SB_ACTIVE_AGENT.id : activeUser().id); 1158 if (external_id == 1) { 1159 external_id = 'SB-1'; 1160 } 1161 OneSignal.User.addTag('user_type', admin ? 'agents' : 'users'); 1162 OneSignal.login(external_id); 1163 } 1164 SBF.event('SBPushNotificationSubscription', event.current); 1165 }); 1166 }); 1167 init_push_notifications = false; 1168 }); 1169 } 1170 }, 1171 1172 closeNotifications: function (index = 0) { 1173 if (this.sw) { 1174 this.sw.getNotifications().then((notifications) => { 1175 if (notifications.length) { 1176 for (let i = 0; i < notifications.length; i += 1) { 1177 notifications[i].close(); 1178 } 1179 } else if (index < 300 && SBF.visibility_status == 'visible') { 1180 setTimeout(() => { 1181 this.closeNotifications(index + 1); 1182 }, 10); 1183 } 1184 }); 1185 } 1186 }, 1187 1188 pushNotification: function (message, interests = false) { 1189 let icon = admin ? SB_ACTIVE_AGENT.profile_image : activeUser().image; 1190 SBF.ajax({ 1191 function: 'push-notification', 1192 title: admin ? SB_ACTIVE_AGENT.full_name : activeUser().name, 1193 message: SBF.strip(message), 1194 icon: icon.indexOf('user.svg') > 0 ? CHAT_SETTINGS.notifications_icon : icon, 1195 interests: interests ? interests : SBChat.getRecipientUserID(), 1196 conversation_id: SBChat.conversation ? SBChat.conversation.id : false 1197 }, (response) => { 1198 return response 1199 }); 1200 } 1201 }, 1202 1203 beautifyAttachmentName: function (name) { 1204 let index = name.indexOf('_'); 1205 return index !== -1 ? name.substring(index + 1) : name; 1206 }, 1207 1208 audio: { 1209 play: function (audio_element, parent = false) { 1210 audio_element = $(audio_element); 1211 let audio = audio_element.parent().find('audio').get(0); 1212 let stopped = audio_element.hasClass('sb-icon-play'); 1213 let audio_time = audio_element.parent().find('.sb-player
1213-time'); 1214 $(parent ? parent : chat).find('audio').each(function () { 1215 $(this).get(0).pause(); 1216 $(this).unbind('ended'); 1217 $(this).parent().find('.sb-player-btn').removeClass('sb-icon-pause').addClass('sb-icon-play'); 1218 }); 1219 if (stopped) { 1220 audio.play(); 1221 audio_element.removeClass('sb-icon-play'); 1222 } else { 1223 audio.pause(); 1224 } 1225 $(audio).unbind('ended'); 1226 $(audio).bind('ended', () => { 1227 audio_element.removeClass('sb-icon-pause').addClass('sb-icon-play'); 1228 }); 1229 audio_element.addClass('sb-icon-' + (stopped ? 'pause' : 'play')); 1230 if (audio.ontimeupdate) { 1231 audio.removeEventListener('timeupdate', audio.ontimeupdate); 1232 audio.removeEventListener('loadedmetadata', audio.onloadedmetadata); 1233 } 1234 if (audio_time) { 1235 audio.ontimeupdate = () => { 1236 audio_time.find('span:first-child').html(SBF.getMinutesSeconds(audio.currentTime.toFixed(0))); 1237 } 1238 audio.onloadedmetadata = () => { 1239 audio_time.find('span:last-child').html(SBF.getMinutesSeconds(audio.duration.toFixed(0))); 1240 } 1241 audio.addEventListener('timeupdate', audio.ontimeupdate); 1242 audio.addEventListener('timeupdate', audio.onloadedmetadata); 1243 } 1244 }, 1245 1246 speed: function (audio_element) { 1247 let parent = $(audio_element).parent();
1248 if (parent.find('.sb-player-btn').hasClass('sb-icon-pause')) { 1249 let element = $(audio_element).find('.sb-player-speed-number'); 1250 let speed = parseFloat(element.html()); 1251 speed += 0.5; 1252 if (speed > 2) { 1253 speed = 1; 1254 } 1255 parent.find('audio').get(0).playbackRate = speed; 1256 element.html(speed); 1257 } 1258 }, 1259 1260 download: function (audio_element) { 1261 window.open($(audio_element).parent().find('audio source').attr('src')); 1262 } 1263 1264 } 1265 } 1266 1267 /* 1268 * ---------------------------------------------------------- 1269 * PUSHER 1270 * ---------------------------------------------------------- 1271 */ 1272 1273 var SBPusher = { 1274 channels: {}, 1275 channels_presence: [], 1276 active: false, 1277 pusher: false, 1278 started: false, 1279 pusher_beams: false, 1280 initialized: false, 1281 online_ids: [], 1282 beams_loaded: false, 1283 1284 // Initialize Pusher 1285 init: function (onSuccess = false) { 1286 if (SBPusher.active) { 1287 if (this.pusher) { 1288 return onSuccess ? onSuccess() : true; 1289 } else if (onSuccess) { 1290 $(window).one('SBPusherInit', () => { 1291 onSuccess(); 1292 }); 1293 } else { 1294 return; 1295 } 1296 this.initialized = true; 1297 if (typeof Pusher === ND) { 1298 $.getScript('https://js.pusher.com/8.2.0/pusher.min.js', () => { 1299 window.Pusher = Pusher; 1300 this.init_2(); 1301 }, true); 1302 } else { 1303 this.init_2(); 1304 } 1305 } 1306 }, 1307 1308 init_2: function () { 1309 this.pusher = new Pusher(admin ? SB_ADMIN_SETTINGS.pusher_key : CHAT_SETTINGS.pusher_key, { 1310 cluster: admin ? SB_ADMIN_SETTINGS.pusher_cluster : CHAT_SETTINGS.pusher_cluster, 1311 channelAuthorization: { 1312 endpoint: SB_URL + '/include/pusher.php', 1313 params: { 1314 login: SBF.loginCookie(), 1315 cloud_user_id: CHAT_SETTINGS.cloud ? CHAT_SETTINGS.cloud.cloud_user_id : false 1316 } 1317 } 1318 }); 1319 SBF.event('SBPusherInit'); 1320 }, 1321 1322 // Initialize Push notifications 1323 initPushNotifications: function () { 1324 if (activeUser() || admin) { 1325 if (this.beams_loaded) { 1326 this.initPushNotifications_2(); 1327 } else { 1328 $.getScript('https://js.pusher.com/beams/2.0.0-beta.0/push-notifications-cdn.js', () => { 1329 this.initPushNotifications_2(); 1330 }, true); 1331 } 1332 } 1333 }, 1334 1335 initPushNotifications_2: function () { 1336 window.navigator.serviceWorker.ready.then((serviceWorkerRegistration) => { 1337 this.pusher_beams = new PusherPushNotifications.Client({ 1338 instanceId: admin ? SB_ADMIN_SETTINGS.push_notifications_id : CHAT_SETTINGS.push_notifications_id, 1339 serviceWorkerRegistration: serviceWorkerRegistration, 1340 }); 1341 SBF.serviceWorker.closeNotifications(); 1342 this.pusher_beams.start().then(() => this.pusher_beams.setDeviceInterests(admin ? [SB_ACTIVE_AGENT.id, 'agents'] : [activeUser().id, 'users'])).catch(console.error); 1343 init_push_notifications = false; 1344 }); 1345 }, 1346 1347 // Start Pusher and Push notifications 1348 start: function () { 1349 if (!admin && !this.started && activeUser()) { 1350 if (this.active) { 1351 this.init(() => { 1352 this.event('client-typing', (response) => { 1353 if (response.user_id == SBChat.agent_id && SBChat.conversation && response.conversation_id == SBChat.conversation.id) { 1354 SBChat.typing(-1, 'start'); 1355 clearTimeout(timeout_typing); 1356 timeout_typing = setTimeout(() => { SBChat.typing(-1, 'stop') }, 1000); 1357 } 1358 }); 1359 this.event('new-message', (response) => { 1360 if (response && activeUser() && response.conversation_id && (!activeUser().getConversationByID(response.conversation_id) || !SBChat.conversation || SBChat.conversation.id != response.conversation_id)) { 1361 SBChat.updateConversations(); 1362 } else { 1363 if (response && response.action && ['message_delete', 'message_update'].includes(response.action)) { 1364 SBChat.id_last_message_conversation = response.message_id - 1; 1365 }
1366 SBChat.update(); 1367 } 1368 }); 1369 this.presence(1, () => { 1370 this.started = true; 1371 SBChat.automations.runAll(); 1372 }); 1373 }); 1374 } 1375 if (CHAT_SETTINGS.push_notifications_users) { 1376 if (CHAT_SETTINGS.push_notifications_provider == 'pusher' || CHAT_SETTINGS.push_notifications_provider != 'onesignal') { // Deprecated: remove || CHAT_SETTINGS.push_notifications != '' 1377 if (typeof Notification != ND && Notification.permission == 'granted') { 1378 this.initPushNotifications(); 1379 } else { 1380 init_push_notifications = true; 1381 } 1382 } 1383 } 1384 } 1385 }, 1386 1387 // Subscribe to a channel 1388 subscribe: function (channel_name, onSuccess = false) { 1389 if (!this.pusher) { 1390 return this.init(() => { this.subscribe(channel_name, onSuccess) }); 1391 } 1392 channel_name = this.cloudChannelRename(channel_name); 1393 let channel = this.pusher.subscribe(channel_name); 1394 channel.bind('pusher:subscription_error', (error) => { 1395 return console.log(error); 1396 }); 1397 channel.bind('pusher:subscription_succeeded', () => { 1398 this.channels[channel_name] = channel; 1399 if (onSuccess) onSuccess(); 1400 }) 1401 }, 1402 1403 // Add event listener for a channel 1404 event: function (event, callback, channel = 'private-user-' + activeUser().id) { 1405 if (!this.pusher) { 1406 return this.init(() => { this.event(event, callback, channel) }); 1407 } 1408 let channel_original = channel; 1409 channel = this.cloudChannelRename(channel); 1410 if (channel in this.channels) { 1411 this.channels[channel].unbind(event); 1412 this.channels[channel].bind(event, (data) => { 1413 callback(data); 1414 }); 1415 } else { 1416 this.subscribe(channel_original, () => { this.event(event, callback, channel_original) }); 1417 } 1418 }, 1419 1420 // Trigger an event 1421 trigger: function (event, data = {}, channel = 'private-user-' + activeUser().id) { 1422 if (event.indexOf('client-') == 0) { 1423 return this.channels[this.cloudChannelRename(channel)].trigger(event, data); 1424 } else { 1425 SBF.ajax({ 1426 function: 'pusher-trigger', 1427 channel: channel, 1428 event: event, 1429 data: data 1430 }, (response) => { 1431 return response 1432 }); 1433 } 1434 }, 1435 1436 // Presence 1437 presence: function (index = 1, onSuccess) { 1438 if (!this.pusher) { 1439 return this.init(() => { this.presence() }); 1440 } 1441 let channel = this.pusher.subscribe(this.cloudChannelRename('presence-' + index)); 1442 channel.bind('pusher:subscription_succeeded', (members) => { 1443 if (members.count > 98) { 1444 return this.subscribe(index + 1); 1445 } 1446 members.each((member) => { 1447 if (this.presenceCheck(member)) { 1448 this.online_ids.push(member.id); 1449 } 1450 }); 1451 SBChat.updateUsersActivity(); 1452 if (onSuccess) { 1453 onSuccess(); 1454 } 1455 }) 1456 channel.bind('pusher:subscription_error', (error) => { 1457 return console.log(error); 1458 }); 1459 channel.bind('pusher:member_added', (member) => { 1460 if (this.presenceCheck(member)) { 1461 this.presenceAdd(member.id); 1462 } 1463 if (admin && SBF.storageTime('online-user-notification-' + member.id, 24)) { 1464 SBAdmin.users.onlineUserNotification(member); 1465 SBF.storageTime('online-user-notification-' + member.id); 1466 } 1467 }); 1468 channel.bind('pusher:member_removed', (member) => { 1469 this.presenceRemove(member.id); 1470 }); 1471 this.channels_presence.push(channel); 1472 if (!admin && CHAT_SETTINGS.slack_active) { 1473 this.event('add-user-presence', (response) => { 1474 this.presenceAdd(response.agent_id) 1475 }); 1476 SBF.ajax({ 1477 function: 'slack-presence', 1478 list: true 1479 }, (response) => { 1480 for (var i = 0; i < response.length; i++) { 1481 this.presenceAdd(response[i]); 1482 } 1483 SBChat.updateUsersActivity(); 1484 }); 1485 } 1486 }, 1487 1488 presenceCheck: function (member) { 1489 let agent = SBF.isAgent(member.info.user_type); 1490 return ((admin && !agent) || (!admin && agent)) && !this.online_ids.includes(member.id); 1491 }, 1492 1493 presenceAdd: function (user_id) { 1494 if (typeof user_id != ND && !this.online_ids.includes(user_id)) { 1495 this.online_ids.push(user_id); 1496 this.presenceUpdateAdmin(user_id); 1497 SBChat.updateUsersActivity(user_id); 1498 } 1499 }, 1500 1501 presenceRemove: function (user_id) { 1502 if (typeof user_id == ND) { 1503 return; 1504 } 1505 let index = this.online_ids.indexOf(user_id); 1506 if (index !== -1) { 1507 this.online_ids.splice(index, 1); 1508 this.presenceUpdateAdmin(user_id); 1509 SBChat.updateUsersActivity(); 1510 } else if (admin) { 1511 global.find(`.sb-conversation-busy[data-agent="${user_id}"]`).remove(); 1512 } 1513 }, 1514 1515 presenceUnsubscribe: function () { 1516 for (var i = 0; i < this.channels_presence.length; i++) { 1517 this.channels_presence[i].unsubscribe(this.cloudCh
1517annelRename('presence-' + (i + 1))); 1518 } 1519 }, 1520 1521 presenceUpdateAdmin: function (user_id) { 1522 if (admin) { 1523 if (global.find('.sb-area-users.sb-active').length) { 1524 SBAdmin.users.update(); 1525 } 1526 if (activeUser() && activeUser().id == user_id) { 1527 SBAdmin.users.updateUsersActivity(); 1528 } 1529 } 1530 }, 1531 1532 cloudChannelRename: function (channel) { 1533 return (CHAT_SETTINGS.cloud || (admin && SB_ADMIN_SETTINGS.cloud)) ? channel + '-' + (admin ? SB_ADMIN_SETTINGS.cloud.cloud_user_id : CHAT_SETTINGS.cloud.cloud_user_id) : channel; 1534 } 1535 } 1536 1537 /* 1538 * ---------------------------------------------------------- 1539 * GLOBAL FUNCTIONS 1540 * ---------------------------------------------------------- 1541 */ 1542 1543 window.SBF = SBF; 1544 window.SBPusher = SBPusher; 1545 window.sb_current_user = sb_current_user; 1546 1547 /* 1548 * ---------------------------------------------------------- 1549 * JQUERY FUNCTIONS 1550 * ---------------------------------------------------------- 1551 */ 1552 1553 $.fn.sbActive = function (show = -1) { 1554 if (show === -1) { 1555 return $(this).hasClass('sb-active'); 1556 } 1557 $(this).setClass('sb-active', show); 1558 return this; 1559 }; 1560 1561 $.fn.sbLoading = function (value = 'check') { 1562 if (value == 'check') { 1563 return $(this).hasClass('sb-loading'); 1564 } else { 1565 $(this).setClass('sb-loading', value); 1566 } 1567 return this; 1568 } 1569 1570 $.fn.sbTogglePopup = function (button = false) { 1571 let showed = true; 1572 if (admin) SBAdmin.open_popup = false; 1573 if ($(this).sbActive()) { 1574 $(this).sbActive(false); 1575 global.removeClass('sb-popup-active'); 1576 showed = false; 1577 } else { 1578 global.addClass('sb-popup-active'); 1579 global.find('.sb-popup').sbActive(false); 1580 if (button) $(this).css('left', $(button).offset().left + 15).sbActive(true); 1581 if (admin) setTimeout(() => { SBAdmin.open_popup = this }, 500); 1582 SBF.deselectAll(); 1583 } 1584 return showed; 1585 }; 1586 1587 $.fn.sbUploadFiles = function (onSuccess, index = false) { 1588 let files = $(this).prop('files'); 1589 for (var i = (index === false ? 0 : index); i < (index === false ? files.length : index + 1); i++) { 1590 let file = files[i]; 1591 let size_mb = file.size / (1024 ** 2); 1592 let max_size = admin ? SB_ADMIN_SETTINGS.max_file_size : CHAT_SETTINGS.max_file_size; 1593 if (size_mb > max_size) { 1594 let message = sb_('Maximum upload size is {R}MB. File size: {R2}MB.').replace('{R}', max_size).replace('{R2}', size_mb.toFixed(2)); 1595 SBChat.is_busy_update = false; 1596 SBChat.busy(false); 1597 if (admin) { 1598 SBAdmin.conversations.busy = false; 1599 SBAdmin.infoPanel(message, 'info'); 1600 } else { 1601 alert(message); 1602 } 1603 } 1604 let form = new FormData(); 1605 form.append('file', file); 1606 SBF.upload(form, onSuccess); 1607 } 1608 $(this).value = ''; 1609 } 1610 1611 $.fn.setProfile = function (name = false, profile_image = false) { 1612 if (SBF.null(name)) { 1613 name = activeUser() ? activeUser().name : ''; 1614 } 1615 if (SBF.null(profile_image)) { 1616 profile_image = activeUser() ? activeUser().image : SB_URL + '/media/user.svg'; 1617 } 1618 if (name) { 1619 $(this).removeClass('sb-profile-empty'); 1620 } 1621 $(this).find('img').attr('src', profile_image); 1622 $(this).find('.sb-name').html(name); 1623 return this; 1624 } 1625 1626 $.fn.setClass = function (class_name, add = true) { 1627 if (add) { 1628 $(this).addClass(class_name); 1629 } else { 1630 $(this).removeClass(class_name); 1631 } 1632 return this; 1633 } 1634 1635 /* 1636 * ---------------------------------------------------------- 1637 * FUNCTIONS 1638 * ---------------------------------------------------------- 1639 */ 1640 1641 function sbDelta(e) { 1642 let delta = e.originalEvent.wheelDelta; 1643 if (typeof delta == ND) { 1644 delta = e.originalEvent.deltaY; 1645 } 1646 if (typeof delta == ND) { 1647 delta = e.originalEvent.detail * -1; 1648 } 1649 return delta; 1650 } 1651 1652 function loading(element) { 1653 if ($(element).sbLoading()) { 1654 return true; 1655 } else { 1656 $(element).sbLoading(true); 1657 } 1658 return false;
1659 } 1660 1661 function storage(key, value = ND) { 1662 return SBF.storage(key, value); 1663 } 1664 1665 function sb_(string) { 1666 return SBF.translate(string); 1667 } 1668 1669 function activeUser(value = -1) { 1670 if (value === -1) { 1671 return window.sb_current_user; 1672 } else { 1673 window.sb_current_user = value; 1674 } 1675 } 1676 1677 function setAudio() { 1678 let volume = admin ? SB_ADMIN_SETTINGS.sound.volume : CHAT_SETTINGS.sound.volume; 1679 if (SBChat.audio && volume) { 1680 SBChat.audio.volume = volume; 1681 } 1682 } 1683 1684 /* 1685 * ---------------------------------------------------------- 1686 * USER 1687 * ---------------------------------------------------------- 1688 */ 1689 1690 class SBUser { 1691 constructor(details = {}, extra = {}) { 1692 this.details = details; 1693 this.extra = extra; 1694 this.conversations = []; 1695 this.processArray(details); 1696 } 1697 1698 get id() { 1699 return this.get('id') ? this.get('id') : this.get('user_id'); 1700 } 1701 1702 get type() { 1703 return this.get('user_type'); 1704 } 1705 1706 get email() { 1707 return this.get('email'); 1708 } 1709 1710 get name() { 1711 return this.details.first_name ? this.details.first_name + (this.details.last_name ? ' ' + this.details.last_name : '') : ''; 1712 } 1713 1714 get nameBeautified() { 1715 let default_name = admin ? SB_ADMIN_SETTINGS.visitor_default_name : CHAT_SETTINGS.visitor_default_name; 1716 return !default_name || (this.details.last_name && this.details.last_name.charAt(0) != '#') ? this.name : default_name; 1717 } 1718 1719 get image() { 1720 return this.get('profile_image'); 1721 } 1722 1723 get language() { 1724 let language = this.getExtra('language'); 1725 if (!language) { 1726 language = this.getExtra('browser_language'); 1727 } 1728 return language ? language.value.toLowerCase() : ''; 1729 } 1730 1731 get(id) { 1732 if (id in this.details && !SBF.null(this.details[id])) { 1733 return this.details[id]; 1734 } 1735 return ''; 1736 } 1737 1738 getExtra(id) { 1739 if (id in this.extra && !SBF.null(this.extra[id])) { 1740 return this.extra[id]; 1741 } 1742 return ''; 1743 } 1744 1745 set(id, value) { 1746 this.details[id] = value; 1747 } 1748 1749 setExtra(id, value) { 1750 this.extra[id] = value; 1751 } 1752 1753 // Initialization 1754 processArray(details) { 1755 if (details && details.details) { 1756 for (var i = 0; i < details.details.length; i++) { 1757 this.setExtra(details.details[i].slug, details.details[i]); 1758 } 1759 delete details.details; 1760 this.details = details; 1761 } 1762 } 1763 1764 // Get user details and extra details 1765 update(onSuccess) { 1766 if (this.id) { 1767 SBF.ajax({ 1768 function: 'get-user', 1769 user_id: this.id, 1770 extra: true 1771 }, (response) => { 1772 this.processArray(response); 1773 onSuccess(response); 1774 SBF.event('SBGetUser', this); 1775 }); 1776 } else { 1777 SBF.error('Missing user ID', 'SBUser.update'); 1778 } 1779 } 1780 1781 // Get user conversations 1782 getConversations(onSuccess = false, exclude_id) { 1783 if (this.id) { 1784 SBF.ajax({ 1785 function: 'get-user-conversations', 1786 user_id: this.id, 1787 exclude_id: exclude_id, 1788 agent: SBF.isAgent(this.type) 1789 }, (response) => { 1790 if (!SBF.errorValidation(response)) { 1791 let conversations = []; 1792 for (var i = 0; i < response.length; i++) { 1793 if (SBChat.isConversationAllowed(response[i].source, response[i].conversation_status_code, response[i].payload)) { 1794 conversations.push(new SBConversation([new SBMessage(response[i])], response[i])); 1795 } 1796 } 1797 this.conversations = conversations; 1798 if (onSuccess) { 1799 onSuccess(conversations); 1800 } 1801 } 1802 }); 1803 } else { 1804 SBF.error('Missing user ID', 'SBUser.getConversations'); 1805 } 1806 } 1807 1808 // Get conversations code 1809 getConversationsCode(conversations = false) { 1810 let code = ''; 1811 let active_conversation_id = SBChat.conversation ? SBChat.conversation.id : -1; 1812 if (!conversations) { 1813 conversations = this.conversations; 1814 } 1815 for (var i = 0; i < conversations.length; i++) { 1816 if (conversations[i] instanceof SBConversation) { 1817 if (!admin && !SBChat.isConversationAllowed(conversations[i].get('source'), conversations[i].status_code, conversations[i].getLastMessage() ? JSON.stringify(conversations[i].getLastMessage().payload()) : false)) { 1818 continue; 1819 } 1820 let red_notifications = 0; 1821 let is_active_conversation = active_conversation_id == conversations[i].id; 1822 if (!admin && !is_active_conversation) { 1823 for (var j = 0; j < SBChat.notifications.length; j++) { 1824 if (SBChat.notifications[j][0] == conversations[i].id) { 1825 red_notifications++; 1826 } 1827 } 1828 } 1829 code += `<li ${is_active_conversation ? 'class="sb-active" ' : ''}data-conversation-status="${is_active_conversation ? 0 : conversations[i].status_code}
1829" data-conversation-id="${conversations[i].id}" data-department="${conversations[i].get('department')}">${conversations[i].getCode()}${red_notifications ? '<span data-count="' + red_notifications + '">' + red_notifications + '</span>' : ''}</li>`; 1830 } else { 1831 SBF.error('Conversation not of type SBConversation', 'SBUser.getConversationsCode'); 1832 } 1833 } 1834 return code; 1835 } 1836 1837 // Get single conversation 1838 getFullConversation(conversation_id = false, onSuccess = false) { 1839 if (conversation_id !== false) { 1840 SBF.ajax({ 1841 function: 'get-conversation', 1842 conversation_id: conversation_id 1843 }, (response) => { 1844 let messages = []; 1845 if (response) { 1846 if (response === 'agent-not-authorized') { 1847 window.location.href = SBF.URL(); 1848 return; 1849 } 1850 for (var i = 0; i < response.messages.length; i++) { 1851 messages.push(new SBMessage(response.messages[i])); 1852 } 1853 } 1854 if (onSuccess) { 1855 onSuccess(new SBConversation(messages, response ? response.details : false)); 1856 } 1857 }); 1858 } else { 1859 SBF.error('Missing conversation ID', 'SBUser.getFullConversation'); 1860 } 1861 } 1862 1863 getConversationByID(conversation_id, index = false) { 1864 for (var i = 0; i < this.conversations.length; i++) { 1865 if (this.conversations[i].id == conversation_id) { 1866 return index ? i : this.conversations[i]; 1867 } 1868 } 1869 return false; 1870 } 1871 1872 // Add a new conversation 1873 addConversation(conversation) { 1874 if (conversation instanceof SBConversation) { 1875 let conversation_id = conversation.id; 1876 let is_new = true; 1877 for (var i = 0; i < this.conversations.length; i++) { 1878 if (this.conversations[i].id == conversation_id) { 1879 this.conversations[i] = conversation; 1880 is_new = false; 1881 break; 1882 } 1883 } 1884 if (is_new) { 1885 this.conversations.unshift(conversation); 1886 } 1887 return is_new; 1888 } else { 1889 SBF.error('Conversation not of type SBConversation', 'SBUser.addConversation'); 1890 } 1891 } 1892 1893 // Remove a conversation 1894 removeConversation(conversation_id) { 1895 let index = this.getConversationByID(conversation_id, true); 1896 if (index !== false) { 1897 this.conversations.splice(index, 1); 1898 } 1899 } 1900 1901 // Get the last conversation 1902 getLastConversation() { 1903 if (this.isConversationsEmpty()) { 1904 return false; 1905 } 1906 for (var i in this.conversations) { 1907 let conversation = this.conversations[i]; 1908 if (SBChat.isConversationAllowed(conversation.get('source'), conversation.status_code, JSON.stringify(conversation.getLastMessage().payload()))) { 1909 return conversation; 1910 } 1911 } 1912 } 1913 1914 // Check if the conversation array is empty 1915 isConversationsEmpty() { 1916 if (!this.conversations.length) { 1917 return true; 1918 } 1919 for (var i in this.conversations) { 1920 let conversation = this.conversations[i]; 1921 if (SBChat.isConversationAllowed(conversation.get('source'), conversation.status_code, JSON.stringify(conversation.getLastMessage().payload()))) { 1922 return false; 1923 } 1924 } 1925 return true; 1926 } 1927 1928 // Check if the extra array is empty 1929 isExtraEmpty() { 1930 return Object.keys(this.extra).length === 0 && this.extra.constructor === Object; 1931 } 1932 1933 // Delete the user 1934 delete(onSuccess) {
1935 if (this.id) { 1936 SBF.ajax({ 1937 function: 'delete-user', 1938 user_id: this.id 1939 }, () => { 1940 SBF.event('SBUserDeleted', this.id); 1941 onSuccess(); 1942 return true; 1943 }); 1944 } else { 1945 SBF.error('Missing user ID', 'SBUser.delete'); 1946 } 1947 } 1948 } 1949 window.SBUser = SBUser; 1950 1951 /* 1952 * ---------------------------------------------------------- 1953 * MESSAGE 1954 * ---------------------------------------------------------- 1955 */ 1956 1957 class SBMessage { 1958 constructor(details = {}) { 1959 this.details = Object.assign({}, details); 1960 let keys = ['message_status_code', 'message_id', 'message_profile_image', 'message_first_name', 'message_last_name', 'message_user_id', 'message_user_type']; 1961 let keys_delete = ['source', 'extra', 'title', 'tags', 'agent_id', 'department', 'last_update_time', 'conversation_creation_time', 'conversation_id', 'conversation_status_code', 'conversation_user_id']; 1962 for (var i = 0; i < keys.length; i++) { 1963 if (details[keys[i]]) { 1964 this.details[keys[i].replace('message_', '')] = details[keys[i]]; 1965 } 1966 delete this.details[keys[i]]; 1967 } 1968 if (this.details.first_name) { 1969 this.details.full_name = this.details.first_name + (this.details.last_name ? ' ' + this.details.last_name : ''); 1970 } 1971 if (details.last_update_time) { 1972 this.details.creation_time = details.last_update_time; 1973 } 1974 for (var i = 0; i < keys_delete.length; i++) { 1975 delete this.details[keys_delete[i]]; 1976 } 1977 let payload = this.get('payload'); 1978 if (payload) { 1979 if (typeof payload === 'string') { 1980 try { 1981 var json = JSON.parse(this.get('payload').replace("\\'", "'")); 1982 if (json && typeof json === 'object') { 1983 payload = json; 1984 } else { 1985 payload = {}; 1986 } 1987 } catch (e) { 1988 payload = {}; 1989 } 1990 } 1991 } else { 1992 payload = {}; 1993 } 1994 this.set('payload', payload); 1995 } 1996 1997 get id() { 1998 return this.get('id'); 1999 } 2000 2001 get attachments() { 2002 return !SBF.null(this.details.attachments) ? JSON.parse(this.details.attachments) : []; 2003 } 2004 2005 get message() { 2006 return admin ? (this.payload('translation') && this.payload('translation-language') == SB_ADMIN_SETTINGS.active_agent_language ? this.payload('translation') : (this.payload('original-message-language') == SB_ADMIN_SETTINGS.active_agent_language ? this.payload('original-message') : this.get('message'))) : this.get('message'); 2007 } 2008 2009 get(id) { 2010 if (id in this.details && !SBF.null(this.details[id])) { 2011 return this.details[id]; 2012 } 2013 return ''; 2014 } 2015 2016 set(id, value) { 2017 this.details[id] = value; 2018 } 2019 2020 payload(key = false, value = false) { 2021 let payload = this.get('payload'); 2022 if (key !== false && value !== false) { 2023 payload[key] = value; 2024 this.set('payload', payload); 2025 } else if (key !== false) { 2026 return key in payload ? payload[key] : (payload.id && payload.id == key ? payload : false); 2027 } 2028 return ['boolean', 'string'].includes(typeof payload) ? [] : payload; 2029 } 2030 2031 getCode() { 2032 let agent = SBF.isAgent(this.details.user_type); 2033 let message = this.message; 2034 let attachments = this.attachments; 2035 let reply = this.payload('reply'); 2036 let admin_menu = admin ? SBAdmin.conversations.messageMenu(agent, message, !reply && !agent && (!SBChat.conversation || ['tg', 'fb', 'w
2036a', ''].includes(SBChat.conversation.get('source')))) : ''; 2037 let attachments_code = ''; 2038 let media_code = ''; 2039 let thumb = (admin && SB_ADMIN_SETTINGS.show_profile_images) || (!admin && ((agent && !CHAT_SETTINGS.hide_agents_thumb) || (!agent && CHAT_SETTINGS.display_users_thumb))) ? `<div class="sb-thumb"><img loading="lazy" src="${this.details.profile_image}"><div class="sb-tooltip"><div>${this.details.full_name}</div></div></div>` : ''; 2040 let css = ((admin && agent) || (!admin && !agent) ? 'sb-right' : '') + (thumb ? ' sb-thumb-active' : ''); 2041 let type = ''; 2042 let name = (!admin && agent && CHAT_SETTINGS.sender_name) || (admin && SB_ADMIN_SETTINGS.sender_name == 'chat-admin') ? `<span class="sb-agent-name">${this.get('full_name')}</span>` : ''; 2043 let delivery_failed = admin ? this.payload('delivery_failed') : false; 2044 2045 if (!message && !attachments.length) { 2046 if (admin) { 2047 message = SBAdmin.conversations.getInfoMessage(this); 2048 } 2049 if (!message) { 2050 return ''; 2051 } else { 2052 css += ' sb-info-message'; 2053 } 2054 } 2055 if (reply && SBChat.conversation && SBChat.conversation.getMessage(reply)) { 2056 reply = SBChat.conversation.getMessage(reply); 2057 let is_agent = SBF.isAgent(reply.get('user_type')); 2058 let text = reply.message; 2059 if (!text) { 2060 text = '<div class="sb-message-attachments">'; 2061 attachments.forEach((attachment) => { 2062 text += `<a>${attachment[0]}</a>`; 2063 }); 2064 text += '</div>'; 2065 } 2066 reply = `<div class="sb-reply-message${is_agent ? ' sb-reply-agent' : ''}"><span>${(is_agent && admin) || (!is_agent && !admin) ? sb_('You') : reply.get('full_name')}</span> ${text}</div>`; 2067 } else { 2068 reply = ''; 2069 } 2070 2071 // Rich Messages 2072 if (agent) { 2073 message = message.replace(/\n/g, '<br>'); 2074 message = message.replace(/`([\s\S]*?)`/g, (match) => { 2075 return match.replace(/\[/g, '['); 2076 }); 2077 let shortcodes = message.match(/\[([^\[\]]*(?:\[[^\[\]]*\][^\[\]]*)*)\]/g) || []; 2078 let is_rich_message = false; 2079 let count = shortcodes.length; 2080 for (var i = 0; i < count; i++) { 2081 let settings = SBRichMessages.shortcode(shortcodes[i]); 2082 if (settings[0]) { 2083 if (settings[0] == 'action') { 2084 message = message.replace(shortcodes[i], ''); 2085 } else { 2086 let rich_message = SBRichMessages.generate(settings[1], settings[0]); 2087 if (rich_message) { 2088 message = message.replace(shortcodes[i], rich_message); 2089 is_rich_message = true; 2090 type = `data-type="${settings[0]}"`; 2091 } 2092 } 2093 } 2094 } 2095 if (is_rich_message) { 2096 css += ' sb-rich-cnt'; 2097 if (count > 1) { 2098 type = 'data-type="multiple"'; 2099 } 2100 } 2101 } else if (message.includes('[rating ')) { 2102 let settings = SBRichMessages.shortcode(message); 2103 message = SBRichMessages.generate(settings[1], settings[0]); 2104 } 2105 let matches = message.includes('data-success') ? [...message.matchAll(/data-success="([^"]*)"/g)].map(match => match[1]) : []; 2106 for (var i = 0; i < matches.length; i++) { 2107 message = message.replace(matches[i], '{R' + i + '}'); 2108 } 2109 message = this.render(message); 2110 for (var i = 0; i < matches.length; i++) { 2111 message = message.replace('{R' + i + '}', matches[i]); 2112 } 2113 2114 // Attachments 2115 if (attachments.length) { 2116 attachments_code = '<div class="sb-message-attachments">'; 2117 for (var i = 0; i < attachments.length; i++) { 2118 let url = attachments[i][1]; 2119 let url_and_name = url + attachments[i][0]; 2120 if (SBF.getFileType(url_and_name) == 'image') { 2121 let size = ''; 2122 if (attachments[i].length > 2) { 2123 size = attachments[i][2].split('|'); 2124 size = `width="${size[0]}" style="aspect-ratio: ${size[0]} / ${size[1]}"`; 2125 } 2126 media_code += `<div class="sb-image${url_and_name.includes('.png') ? ' sb-image-png' : (url.includes('sticker_') ? ' sb-image-sticker' : '')}"><img loading="lazy" src="${url}" ${size}/></div>`; 2127 } else if (SBF.getFileType(url_and_name) == 'audio' || url.includes('voice_message') || url.includes('audioclip')) { 2128 if ((admin && !SB_ADMIN_SETTINGS.speech_recognition) || (!admin && !CHAT_SETTINGS.speech_recognition)) { 2129 message = ''; 2130 } 2131 attachments_code += `<div class="sb-player">
2131<div class="sb-player-btn sb-icon-play"></div><div class="sb-player-speed"><div class="sb-player-speed-number">1</div><div class="sb-icon-close"></div></div><div class="sb-player-download sb-icon-arrow-down"></div><audio><source src="${url}" type="audio/mpeg"></audio></div>`; 2132 } else if (url) { 2133 attachments_code += `<a rel="noopener" target="_blank" data-type="${SBF.getFileIcon(url)}" href="${url}">${SBF.beautifyAttachmentName(attachments[i][0])}</a>`; 2134 } 2135 } 2136 attachments_code += '</div>'; 2137 } 2138 2139 // Message creation 2140 let message_text = (delivery_failed ? SBAdmin.conversations.getDeliveryFailedMessage(delivery_failed) : '') + (name + message + media_code).trim(); 2141 return !message_text && !attachments_code ? '' : `<div data-id="${this.details.id}" class="${css}" ${type}>${thumb}${reply}<div class="sb-cnt"><div class="sb-message${media_code && !message ? ' sb-message-media' : ''}"${delivery_failed ? ' style="opacity:.7"' : ''}>${message_text}</div>${attachments_code}<div class="sb-time">${SBF.beautifyTime(this.details.creation_time, true)}${admin && agent && this.details.status_code == 2 ? '<i class="sb-icon-check"></i>' : ''}</div></div>${admin_menu}</div>`; 2142 } 2143 2144 render(message = false) { 2145 if (message === false) { 2146 message = '' + this.details.message; 2147 } 2148 let len = message.length; 2149 2150 // Code block 2151 let codes = message.match(/```([\s\S]*?)```/g) || []; 2152 for (var i = 0; i < codes.length; i++) { 2153 message = message.replace(codes[i], '[code-' + i + ']'); 2154 } 2155 2156 // Breakline 2157 message = message.replace(/(?:\r\n|\r|\n)/g, '<br>'); 2158 2159 // Bold 2160 message = message.replace(/\*([^\**]+)\*/g, "<b>$1</b>"); 2161 2162 // Italic 2163 message = message.replace(/__(.+?)__/g, "<i>$1</i>"); 2164 2165 // Strikethrough 2166 message = message.replace(/\~([^\~~]+)\~/g, "<del>$1</del>"); 2167 2168 // Code 2169 message = message.replace(/\`([^\``]+)\`/g, "<code>$1</code>"); 2170 2171 // Single emoji 2172 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])/)) { 2173 message = `<span class="emoji-large">${message}</span>`; 2174 } 2175 2176 // Links 2177 if (message.includes('](http')) { 2178 let temp = message.split('['); 2179 message = ''; 2180 for (var i = 0; i < temp.length; i++) { 2181 if (temp[i].includes('](http')) { 2182 temp[i] = temp[i].substring(temp[i].indexOf('](') + 2, temp[i].indexOf(')')); 2183 } 2184 message += temp[i]; 2185 } 2186 } 2187 if (message.includes('www.')) { 2188 message = message.replaceAll('www.', 'https://www.').replaceAll('https://https:', 'https:').replaceAll('http://https:', 'http:'); 2189 } 2190 let replace = [['href="http', '[L1]'], ['src="http', '[L2]'], ['url("http', '[L3]'], ['url(\'http', '[L4]'], ['extra="http', '[L5]'], ['data-link="http', '[L6]'], ['data-value="http', '[L7]']]; 2191 for (var i = 0; i < replace.length; i++) { 2192 message = message.replaceAll(replace[i][0], replace[i][1]); 2193 } 2194 if (message.includes('http')) { 2195 message = message.autoLink({ 2196 target: '_blank', 2197 callback: function (url) {
2198 return url.includes('#sb-') ? `<a href="${url.split('#sb-')[0]}" target="_blank">${url.split('#sb-')[1].replaceAll('--', ' ')}</a>` : null; 2199 } 2200 }); 2201 } 2202 for (var i = 0; i < replace.length; i++) { 2203 message = message.replaceAll(replace[i][1], replace[i][0]); 2204 } 2205 2206 // Code block restore 2207 for (var i = 0; i < codes.length; i++) { 2208 message = message.replace('[code-' + i + ']', '<pre>' + $.trim($.trim(codes[i].replace(/```<br>/g, '```').replace(/<br>```/g, '```')).replace(/```/g, '').replace(/(?:\r\n|\r|\n)/g, '<br>')) + '</pre>'); 2209 } 2210 2211 return message.replace(/&lt;/g, '<'); 2212 } 2213 2214 strip(message = false) { 2215 return SBF.strip(message === false ? '' + this.details.message : message); 2216 } 2217 } 2218 window.SBMessage = SBMessage; 2219 2220 /* 2221 * ---------------------------------------------------------- 2222 * CONVERSATION 2223 * ---------------------------------------------------------- 2224 */ 2225 2226 class SBConversation { 2227 constructor(messages, details) { 2228 this.details = SBF.null(details) ? {} : details; 2229 if (Array.isArray(messages)) { 2230 this.messages = []; 2231 if (messages.length) { 2232 if (messages[0] instanceof SBMessage) { 2233 this.messages = messages; 2234 } else { 2235 SBF.error('Messages not of type SBMessage', 'SBConversation.constructor'); 2236 } 2237 } 2238 } else { 2239 SBF.error('Message array not of type Array', 'SBConversation.constructor'); 2240 } 2241 let keys = ['conversation_id', 'conversation_user_id', 'conversation_first_name', 'conversation_last_name', 'conversation_profile_image', 'conversation_user_type', 'conversation_creation_time', 'conversation_status_code']; 2242 let keys_delete = ['payload']; 2243 for (var i = 0; i < keys.length; i++) { 2244 if (details[keys[i]]) { 2245 this.details[keys[i].replace('conversation_', '')] = details[keys[i]]; 2246 } 2247 delete this.details[keys[i]]; 2248 } 2249 for (var i = 0; i < keys_delete.length; i++) { 2250 delete this.details[keys_delete[i]]; 2251 } 2252 if (details) { 2253 this.details.tags = 'tags' in details ? (typeof details.tags === 'string' ? details.tags.split(',') : details.tags) : []; 2254 } 2255 } 2256 2257 get id() { 2258 return this.get('id'); 2259 } 2260 2261 get status_code() { 2262 return this.get('status_code'); 2263 } 2264 2265 get(id) { 2266 if (id in this.details && !SBF.null(this.details[id])) { 2267 return this.details[id]; 2268 } 2269 if (id == 'title') { 2270 if (this.details.title) { 2271 return this.details.title; 2272 } else if (this.details.first_name) { 2273 return this.details.first_name + ' ' + this.details.last_name; 2274 } else if (this.messages.length) { 2275 return this.messages[0].get('full_name'); 2276 } 2277 } 2278 return ''; 2279 } 2280 2281 set(id, value) { 2282 this.details[id] = value; 2283 } 2284 2285 getMessage(id) { 2286 for (var i = 0; i < this.messages.length; i++) { 2287 if (this.messages[i].id == id) { 2288 this.messages[i].set('index', i); 2289 return this.messages[i]; 2290 } 2291 } 2292 return false; 2293 } 2294 2295 getLastMessage(include_hidden_messages = false) { 2296 let index = this.messages.length - 1; 2297 if (include_hidden_messages) { 2298 return index > -1 ? this.messages[index] : false; 2299 } 2300 for (var i = index; i > -1; i--) { 2301 if (this.messages[i].message || this.messages[i].attachments.length || this.messages[i].payload('preview')) { 2302 return this.messages[i]; 2303 } 2304 } 2305 return false;
2306 } 2307 2308 getLastUserMessage(index = false, agent = false) { 2309 if (index === false) { 2310 index = this.messages.length - 1; 2311 } 2312 for (var i = index; i > -1; i--) { 2313 let message = this.messages[i]; 2314 let user_type = message.get('user_type'); 2315 if ((message.message || message.attachments.length) && ((!agent && !SBF.isAgent(user_type)) || (agent === true && (user_type == 'agent' || user_type == 'admin')) || (agent == 'bot' && user_type == 'bot') || (agent == 'no-bot' && user_type != 'bot') || (agent == 'all' && SBF.isAgent(user_type)))) { 2316 this.messages[i].set('index', i - 1); 2317 return this.messages[i]; 2318 } 2319 } 2320 return false; 2321 } 2322 2323 getNextMessage(message_id, user_type = false) { 2324 let count = this.messages.length; 2325 for (var i = 0; i < count; i++) { 2326 if (this.messages[i].id == message_id && i < (count - 1)) { 2327 for (var j = i + 1; j < count; j++) { 2328 let message = this.messages[j]; 2329 let message_user_type = message.get('user_type'); 2330 let next_message = this.messages[i + 1]; 2331 if (!user_type || (user_type == 'agent' && SBF.isAgent(message_user_type)) || (user_type == 'user' && !SBF.isAgent(message_user_type))) { 2332 return next_message; 2333 } 2334 } 2335 break; 2336 } 2337 } 2338 return false; 2339 } 2340 2341 getUserMessages(user_type = 'user') { 2342 let results = []; 2343 let checks = user_type == 'user' ? ['visitor', 'lead', 'user'] : (user_type == 'agents' ? ['agent', 'admin'] : ['bot']); 2344 for (var i = 0; i < this.messages.length; i++) { 2345 if (checks.includes(this.messages[i].get('user_type'))) { 2346 this.messages[i].set('index', i); 2347 results.push(this.messages[i]); 2348 } 2349 } 2350 return results; 2351 } 2352 2353 updateMessage(id, message) { 2354 if (message instanceof SBMessage) { 2355 for (var i = 0; i < this.messages.length; i++) { 2356 if (this.messages[i].id == id) { 2357 this.messages[i] = message; 2358 return true; 2359 } 2360 } 2361 } else { 2362 SBF.error('Message not of type SBMessage', 'SBConversation.updateMessage'); 2363 } 2364 return false; 2365 } 2366 2367 addMessages(messages) { 2368 if (Array.isArray(messages)) { 2369 for (var i = 0; i < messages.length; i++) { 2370 if (messages[i] instanceof SBMessage) { 2371 this.messages.push(messages[i]); 2372 } 2373 } 2374 } else { 2375 if (messages instanceof SBMessage) { 2376 this.messages.push(messages); 2377 } else { 2378 SBF.error('Messages not of type SBMessage', 'SBConversation.addMessages()'); 2379 } 2380 } 2381 return this; 2382 } 2383 2384 getCode(text_only = false) { 2385 let message = this.getLastMessage(); 2386 if (message) { 2387 let text = message.message; 2388 if (!text && message.payload('preview')) { 2389 text = message.payload('preview'); 2390 } 2391 if (admin) { 2392 text = message.payload().preview ? message.payload().preview : text; 2393 } 2394 text = text.replace(/(\r\n|\n|\r)/gm, ' '); 2395 if (text.indexOf('[') !== false) { 2396 let shortcodes = text.match(/\[.+?\]/g) || []; 2397 if (shortcodes.length) { 2398 let shortcode = SBRichMessages.shortcode(shortcodes[0]); 2399 if (shortcode[0]) { 2400 text = text.replace(shortcodes[0], shortcode[0] == 'action' ? '' : sb_(shortcode[1].message ? shortcode[1].message : (shortcode[1].title ? shortcode[1].title : (shortcode[1].name && shortcode[1].name != 'false' && shortcode[1].name != 'true' ? shortcode[1].name : (shortcode[1].link ? shortcode[1].link : (shortcode[1].values ? shortcode[1].values.replaceAll(',', ', ').replaceAll(' ', ' ') : (shortcode[1].options ? shortcode[1].options.replaceAll(',', ', ').replaceAll(' ', ' ') : sb_(SBF.slugToString(shortcode[0]))))))))); 2401 } 2402 } 2403 } 2404 if (!text && message.attachments.length) { 2405 for (var i = 0; i < message.attachments.length; i++) { 2406 text += message.attachments[i][0] + ' '; 2407 } 2408 } 2409 text = SBF.strip(text); 2410 if (text.length > 114) { 2411 text = text.substr(0, 114) + ' ...'; 2412 } 2413 if (text_only) { 2414 return text; 2415 } 2416 let title = this.get('title'); 2417 if (!title || (activeUser() && activeUser().name == title) || (tickets && CHAT_SETTINGS.tickets_conversations_title_user)) { 2418 title = activeUser() && message.get('user_id') == activeUser().id ? sb_('You') : message.get('full_name'); 2419 } 2420 if (SBF.getFileType(title) == 'image') { 2421 title = `<img src="${title}" />
2421${title.includes('t=') ? ` <span>${new URL(title).searchParams.get('t')}</span>` : ''}`; 2422 } 2423 return `<div class="sb-conversation-item" data-user-id="${this.get('user_id')}"><img loading="lazy" 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}</div></div>`; 2424 } 2425 return ''; 2426 } 2427 2428 deleteMessage(id) { 2429 for (var i = 0; i < this.messages.length; i++) { 2430 if (this.messages[i].id == id) { 2431 this.messages.splice(i, 1); 2432 return true; 2433 } 2434 } 2435 return false; 2436 } 2437 2438 searchMessages(search, exact_match = false) { 2439 let results = []; 2440 for (var i in this.messages) { 2441 let message = this.messages[i].message; 2442 let payload = JSON.stringify(this.messages[i].payload()); 2443 if ((exact_match && (message == search || payload == search)) || (!exact_match && (message.includes(search) || payload.includes(search)))) { 2444 this.messages[i].set('index', i); 2445 results.push(this.messages[i]); 2446 } 2447 } 2448 return results; 2449 } 2450 2451 getAttachments() { 2452 let list = []; 2453 for (var i = 0; i < this.messages.length; i++) { 2454 let attachments = this.messages[i].attachments; 2455 for (var j = 0; j < attachments.length; j++) { 2456 let link = attachments[j][1]; 2457 list.push([attachments[j][0], link, link.substr(link.lastIndexOf('.') + 1), this.messages[i].id]); 2458 } 2459 } 2460 return list; 2461 } 2462 2463 updateMessagesStatus(ids = false) { 2464 if (ids) { 2465 for (var i = 0; i < this.messages.length; i++) { 2466 let id = this.messages[i].id; 2467 if (ids.includes(id)) { 2468 let div = chat.find(`[data-id="${id}"] .sb-time`); 2469 if (!div.find('i').length) { 2470 div.append('<i class="sb-icon-check"></i>'); 2471 } 2472 } 2473 } 2474 } else if (!admin && SBF.visibility_status == 'visible') { 2475 ids = []; 2476 for (var i = 0; i < this.messages.length; i++) { 2477 let message = this.messages[i]; 2478 if (SBF.isAgent(message.get('user_type')) && message.get('status_code') != 2) { 2479 ids.push(message.id); 2480 message.set('status_code', 2); 2481 } 2482 } 2483 if (ids.length) { 2484 SBF.ajax({ function: 'update-messages-status', message_ids: ids }); 2485 } 2486 } 2487 } 2488 } 2489 window.SBConversation = SBConversation; 2490 2491 /* 2492 * ---------------------------------------------------------- 2493 * CHAT 2494 * ---------------------------------------------------------- 2495 */ 2496 2497 var SBChat = { 2498 emoji_options: { range: 0, range_limit: 47, list: [], list_now: [], touch: false }, 2499 initialized: false, 2500 editor_listening: false, 2501 conversation: false, 2502 is_busy: false, 2503 is_busy_update: false, 2504 is_busy_populate: false, 2505 chat_open: false, 2506 real_time: false, 2507 agent_id: -1, 2508 agent_online: false, 2509 user_online: false, 2510 expanded: false, 2511 main_header: true, 2512 start_header: false, 2513 desktop_notifications: false, 2514 flash_notifications: false, 2515 id_last_message: 0, 2516 id_last_message_conversation: 0, 2517 datetime_last_message_conversation: '2000-01-01 00:00:00', 2518 audio: false, 2519 audio_interval: false, 2520 tab_active: true, 2521 notifications: storage('notifications') ? storage('notifications') : [], 2522 typing_settings: { typing: false, sent: false, timeout: false }, 2523 email_sent: false, 2524 dashboard: false, 2525 articles: false, 2526 articles_allowed_ids: false, 2527 articles_category: false, 2528 skip: false, 2529 queue_interval: false, 2530 departments: false, 2531 default_department: null, 2532 default_agent: null, 2533 default_tags: null, 2534 offline_message_set: false, 2535 label_date: false, 2536 label_date_show: false, 2537 2538 // Send a message 2539 sendMessage: function (user_id = -1, message = '', attachments = [], onSuccess = false, payload = false, conversation_status_code = false) { 2540 let is_dialogflow_human_takeover = dialogflow_human_takeover && SBApps.dialogflow.active(); 2541 let is_return = false;
2542 let conversation = this.conversation; 2543 let reply_id = chat_editor.find('> [data-reply]').attr('data-reply'); 2544 let edit_message_id = chat_editor.find('> [data-edit-message]').attr('data-edit-message'); 2545 if (!payload) { 2546 payload = {}; 2547 } 2548 2549 // Check settings and contents 2550 if (!activeUser() && !admin) { 2551 this.addUserAndLogin(() => { 2552 return this.sendMessage(user_id, message, attachments, onSuccess, payload); 2553 }, true); 2554 this.busy(true); 2555 return; 2556 } 2557 if (!conversation) { 2558 let last_conversation = admin ? false : activeUser().getLastConversation(); 2559 if (last_conversation && force_action != 'new-conversation' && (!SBChat.default_department || SBChat.default_department == last_conversation.get('department')) && (!SBChat.default_agent || SBChat.default_agent == last_conversation.get('agent_id'))) { 2560 this.openConversation(last_conversation.id); 2561 this.setConversation(last_conversation); 2562 force_action = false; 2563 } else { 2564 this.newConversation(conversation_status_code, user_id, '', [], (admin && SB_ACTIVE_AGENT.department ? SB_ACTIVE_AGENT.department : null), null, () => { 2565 return this.sendMessage(user_id, message, attachments, onSuccess, payload); 2566 }); 2567 this.busy(true); 2568 return; 2569 } 2570 } 2571 this.calculateLabelDateFirst(); 2572 if (user_id == -1) { 2573 user_id = admin ? SB_ACTIVE_AGENT.id : activeUser().id; 2574 } 2575 let is_user = user_id != bot_id; 2576 if (!message && !attachments.length) { 2577 message = chat_textarea.val().trim(); 2578 chat_editor.find('.sb-attachments > div').each(function () { 2579 let attachment = [$(this).attr('data-name'), $(this).attr('data-value')]; 2580 if ($(this).attr('data-size')) { 2581 attachment.push($(this).attr('data-size')); 2582 } 2583 attachments.push(attachment); 2584 }); 2585 if (admin && SBAdmin.must_translate && message) { 2586 SBApps.dialogflow.translate([message], activeUser().language, (response) => { 2587 if (response.length) { 2588 let language = admin ? SB_ADMIN_SETTINGS.active_agent_language : activeUser().language; 2589 if (!payload) { 2590 payload = {}; 2591 } 2592 payload['original-message'] = message; 2593 payload['original-message-language'] = language; 2594 if (response[0]) { 2595 message = response[0]; 2596 } 2597 } 2598 this.sendMessage(user_id, message, attachments, onSuccess, payload, conversation_status_code); 2599 }); 2600 is_return = true; 2601 } 2602 } 2603 this.busy(true); 2604 if (is_user) { 2605 chat_textarea.val('').css('height', ''); 2606 chat_editor.find('.sb-attachments').html(''); 2607 } 2608 chat_editor.sbActive(false); 2609 if (is_return) { 2610 return; 2611 } 2612 if (conversation_status_code === false && user_id == bot_id) { 2613 conversation_status_code = 'skip'; 2614 } 2615 if (!admin && is_user && !is_dialogflow_human_takeover) { 2616 conversation_status_code = 2; 2617 } 2618 if (reply_id) { 2619 payload.reply = message; 2620 } 2621 2622 // Send message 2623 if (message || attachments.length || payload) { 2624 let message_response = { user_id: user_id, user: activeUser(), conversation_id: conversation.id, conversation: conversation, conversation_status_code: conversation_status_code, attachments: attachments, payload: payload }; 2625 SBF.ajax({ 2626 function: edit_message_id ? 'update-message' : 'send-message', 2627 user_id: user_id, 2628 conversation_id: conversation.id, 2629 message: message, 2630 attachments: attachments, 2631 conversation_status_code: conversation_status_code, 2632 queue: !admin && CHAT_SETTINGS.queue && is_user, 2633 payload: payload, 2634 recipient_id: admin ? activeUser().id : false, 2635 message_id: edit_message_id 2636 }, (response) => { 2637 let send_slack = admin || !is_dialogflow_human_takeover || response.human_takeover_active 2638 2639 // Update the dashboard conversations area 2640 if (!admin && user_id == bot_id) { 2641 if (this.dashboard) { 2642 this.updateConversations(); 2643 } else if (!this.chat_open) { 2644 this.updateNotifications(conversation.id, response.id); 2645 } 2646 } 2647 2648 // Update the chat current conversation 2649 if ((admin && !this.user_online) || (!admin && !this.agent_online)) { 2650 this.update(); 2651 } 2652 2653 // Follow up and offline messages 2654 if (!admin && is_user && !dialogflow_human_takeover) { 2655 this.followUp(); 2656 this.offlineMessage(); 2657 } 2658 2659 // Chatbot 2660 if (!admin && is_user && (!payload || payload.id != 'sb-human-takeover')) { 2661 SBApps.dialogflow.message(message, attachments); 2662 } 2663 2664 // Slack and visitor to lead 2665 if (!admin && is_user && activeUser().type == 'visitor') { 2666 SBF.ajax({ function: 'update-user-to-lead', user_id: user_id }, () => { 2667 activeUser().set('user_type', 'lead'); 2668 if (CHAT_SETTINGS.slack_active && send_slack) { 2669 SBApps.slack.message(user_id, activeUser().name, activeUser().image, message, attachments); 2670 } 2671 }); 2672 } else if (send_slack && !this.skip) { 2673 if (admin && SB_ADMIN_SETTINGS.slack_active) { 2674 SBApps.slack.message(activeUser().id, SB_ACTIVE_AGENT.full_name, SB_ACTIVE_AGENT.profile_image, message, attachments); 2675 } else if (CHAT_SETTINGS.slack_active) { 2676 SBApps.slack.message(activeUser().id, (is_user ? activeUser().name : CHAT_SETTINGS.bot_name), (is_user ? activeUser().image : CHAT_SETTINGS.bot_image), message, attachments); 2677 } 2678 } 2679 2680 // Language detection 2681 if (is_user && CHAT_SETTINGS.language_detection && conversation && message.split(' ').length && message.length > 3 && !SBF.storage('language-detection-completed')) { 2682 SBF.ajax({ function: 'google-language-detection-update-user', user_id: user_id, string: message, token: SBApps.dialogflow.token }, (response) => { 2683 if (response) { 2684 CHAT_SETTINGS.translations = response; 2685 } 2686 }); 2687 SBF.storage('language-detection-completed', true); 2688 } 2689 2690 // Articles 2691 if (this.articles && !admin && CHAT_SETTINGS.articles && !CHAT_SETTINGS.office_hours && !this.isInitDashboard()) { 2692 setTimeout(() => { 2693 if (this.conversation && conversation.id == this.conversation.id) { 2694 this.sendMessage(bot_id, '[articles]'); 2695 this.scrollBottom(); 2696 this.articles = false;
2697 } 2698 }, 5000); 2699 } 2700 2701 // Queue 2702 if (response.queue) { 2703 this.queue(this.conversation.id); 2704 } 2705 2706 // Events 2707 message_response.message = response.message; 2708 message_response.message_id = response.id; 2709 SBF.event(edit_message_id ? 'SBMessageUpdated' : 'SBMessageSent', message_response); 2710 if (tickets) { 2711 SBTickets.onMessageSent(); 2712 } 2713 if (onSuccess) { 2714 onSuccess(message_response); 2715 } 2716 if (response.notifications && response.notifications.length) { 2717 SBF.event('SBNotificationsSent', response.notifications); 2718 } 2719 2720 // Miscellaneous 2721 if (this.skip) { 2722 this.skip = false; 2723 } 2724 if (admin) { 2725 SBAdmin.conversations.previous_editor_text = false; 2726 } 2727 if (edit_message_id) { 2728 chat.find(`[data-id="${edit_message_id}"] .sb-message`).html(message); 2729 SBChat.cancelEdit(); 2730 } 2731 2732 this.busy(false); 2733 }); 2734 2735 // Display the message as sending in progress 2736 if (is_user && !edit_message_id) { 2737 message = SBF.escape(message); 2738 chat.append((new SBMessage({ id: 'sending', profile_image: (admin ? SB_ACTIVE_AGENT.profile_image : activeUser().image), full_name: admin ? SB_ACTIVE_AGENT.full_name : activeUser().name, creation_time: '0000-00-00 00:00:00', message: message.replaceAll('<', '<'), user_type: (admin ? 'agent' : 'user') })).getCode().replace('<div class="sb-time"></div>', `<div class="sb-time">${sb_('Sending')}<i></i></div>`)); 2739 } 2740 if (!this.dashboard && (is_user || this.isBottom())) { 2741 this.scrollBottom(); 2742 } 2743 } else { 2744 this.busy(false); 2745 } 2746 }, 2747 2748 // Update message 2749 updateMessage: function (message_id, message = '') { 2750 SBF.ajax({ 2751 function: 'update-message', 2752 message_id: message_id, 2753 message: message 2754 }); 2755 }, 2756 2757 // Email notifications 2758 sendEmail: function (message, attachments, send_to_active_user = false, onSuccess = false) { 2759 let recipient_id = send_to_active_user ? (send_to_active_user === true ? activeUser().id : send_to_active_user) : this.getRecipientUserID(); 2760 if (!admin && !isNaN(recipient_id) && this.agent_online) { 2761 return false; 2762 } 2763 SBF.ajax({ 2764 function: 'create-email', 2765 recipient_id: recipient_id, 2766 sender_name: admin ? (send_to_active_user ? SB_ACTIVE_AGENT.full_name : activeUser().name) : (send_to_active_user ? CHAT_SETTINGS.bot_name : activeUser().name), 2767 sender_profile_image: admin ? (send_to_active_user ? SB_ACTIVE_AGENT.profile_image : activeUser().name) : (send_to_active_user ? CHAT_SETTINGS.bot_image : activeUser().image), 2768 message: message, 2769 attachments: attachments, 2770 department: this.conversation ? this.conversation.get('department') : false, 2771 conversation_id: this.conversation ? this.conversation.id : false 2772 }, (response) => { 2773 if (onSuccess) onSuccess(response); 2774 }); 2775 }, 2776 2777 // SMS notifications 2778 sendSMS: function (message) { 2779 let recipient_id = this.getRecipientUserID(); 2780 if (!admin && !isNaN(recipient_id) && this.agent_online) return false; 2781 SBF.ajax({ 2782 function: 'send-sms', 2783 to: recipient_id, 2784 message: message, 2785 conversation_id: this.conversation ? this.conversation.id : false 2786 }, (response) => { 2787 if (response.status == 'sent' || response.status == 'queued') { 2788 SBF.event('SBSMSSent', { recipient_id: this.getRecipientUserID(), message: message, response: response }); 2789 } else if (response.message) { 2790 SBF.error(response.message, 'SBChat.sendSMS'); 2791 } 2792 }); 2793 }, 2794 2795 // Desktop notifications 2796 desktopNotification: function (title, message, icon, conversation_id = false, user_id = false) { 2797 if (Notification.permission !== 'granted') { 2798 Notification.requestPermission(); 2799 } else { 2800 let notify = SBF.serviceWorker.sw.showNotification(title, { 2801 body: SBF.strip(message), 2802 icon: icon.indexOf('user.svg') > 0 ? CHAT_SETTINGS.notifications_icon : icon 2803 }); 2804 notify.onclick = () => { 2805 if (admin) { 2806 if (conversation_id) { 2807 SBAdmin.conversations.openConversation(conversation_id, user_id == false ? activeUser().id : user_id); 2808 SBAdmin.conversations.update(); 2809 } else if (user_id) { 2810 SBAdmin.profile.show(user_id); 2811 } 2812 } else { 2813 this.start(); 2814 } 2815 window.focus(); 2816 } 2817 } 2818 }, 2819 2820 // Return the recipient user ID 2821 getRecipientUserID: function () { 2822 return admin ? activeUser().id : (this.lastAgent(false) ? this.lastAgent(false).user_id : (SBF.null(this.conversation.get('agent_id')) ? (SBF.null(this.conversation.get('department')) ? 'agents' : 'department-' + this.conversation.get('department')) : this.conversation.get('agent_id'))); 2823 }, 2824 2825 // Editor submit message 2826 submit: function () { 2827 if (!this.is_busy) { 2828 if (audio_recorder_dom && audio_recorder_dom.sbActive()) { 2829 let button = audio_recorder_dom.find('.sb-btn-mic'); 2830 audio_recorder_dom.sbActive(false); 2831 if (button.hasClass('sb-icon-pause')) { 2832 button.click(); 2833 } 2834 setTimeout(() => { 2835 let form = new FormData(); 2836 let source = this.conversation ? this.conversation.get('source') : false;
2837 if (source == 'wa' || audio_recorder_chunks.length) { 2838 form.append('file', new File([source == 'wa' ? SBAudioRecorder.blob() : new Blob(audio_recorder_chunks, { type: source == 'ig' ? 'audio/wav' : 'audio/mp3' })], 'voice_message.' + (source == 'ig' ? 'wav' : 'mp3'))); 2839 SBF.upload(form, (response) => { 2840 SBChat.uploadResponse(response); 2841 this.submit(); 2842 }); 2843 } else { 2844 this.submit(); 2845 } 2846 audio_recorder_dom.find('.sb-icon-close').click(); 2847 }, 100); 2848 return; 2849 } 2850 this.sendMessage(); 2851 if (CHAT_SETTINGS.cron_email_piping_active) { 2852 setTimeout(() => { 2853 SBF.ajax({ function: 'email-piping' }); 2854 CHAT_SETTINGS.cron_email_piping_active = true; 2855 }, 60000); 2856 CHAT_SETTINGS.cron_email_piping_active = false; 2857 } 2858 if (init_push_notifications) { 2859 SBF.serviceWorker.initPushNotifications(); 2860 } 2861 if (admin) { 2862 SBAdmin.conversations.setStatus(1, false, true); 2863 } 2864 SBChat.cancelReply(); 2865 } 2866 }, 2867 2868 // Initialize the chat 2869 initChat: function () { 2870 if (admin) return; 2871 SBF.getActiveUser(true, () => { 2872 let active = activeUser() !== false; 2873 let user_type = active ? activeUser().type : false;
2874 if (!tickets && CHAT_SETTINGS.popup && !storage('popup') && (!mobile || !CHAT_SETTINGS.popup_mobile_hidden)) { 2875 this.popup(); 2876 } 2877 SBChat.automations.runAll(); 2878 if (!tickets && CHAT_SETTINGS.privacy && !CHAT_SETTINGS.registration_required && !storage('privacy-approved')) { 2879 this.privacy(); 2880 return; 2881 } 2882 if (typeof Notification !== ND && !CHAT_SETTINGS.push_notifications_users && (['all', 'users'].includes(CHAT_SETTINGS.desktop_notifications) || (admin && CHAT_SETTINGS.desktop_notifications == 'agents'))) { 2883 this.desktop_notifications = true; 2884 } 2885 if (['all', 'users'].includes(CHAT_SETTINGS.flash_notifications) || (admin && CHAT_SETTINGS.flash_notifications == 'agents')) { 2886 this.flash_notifications = true; 2887 } 2888 if (this.registration(true) && !tickets) { 2889 this.registration(); 2890 if (!active && CHAT_SETTINGS.visitors_registration) { 2891 this.addUserAndLogin(); 2892 } 2893 return; 2894 } 2895 if (!active && (typeof SB_WP_WAITING_LIST !== ND || CHAT_SETTINGS.visitors_registration || CHAT_SETTINGS.welcome || tickets || CHAT_SETTINGS.flow_on_load) && (!tickets || !CHAT_SETTINGS.tickets_registration_required)) { 2896 this.addUserAndLogin(() => { 2897 this.welcome(); 2898 SBApps.dialogflow.flowOnLoad(); 2899 SBApps.woocommerce.waitingList(); 2900 this.finalizeInit(); 2901 }); 2902 } else if (!this.conversation && active) { 2903 this.populateConversations(); 2904 } else { 2905 this.finalizeInit(); 2906 } 2907 if (CHAT_SETTINGS.header_name && active && user_type == 'user' && !tickets) { 2908 chat_header.find('.sb-title').html(sb_(CHAT_SETTINGS.header_name).replace('{user_name}', activeUser().nameBeautified)); 2909 } 2910 this.welcome(); 2911 if (!SBPusher.active) { 2912 setInterval(() => { 2913 this.updateConversations(); 2914 this.updateUsersActivity(); 2915 }, 10200); 2916 } 2917 SBApps.dialogflow.flowOnLoad(); 2918 SBApps.woocommerce.waitingList(); 2919 this.scrollBottom(true); 2920 }); 2921 }, 2922 2923 finalizeInit: function () { 2924 if (!this.initialized) { 2925 main.attr('style', ''); 2926 if (!admin && !tickets) { 2927 if (this.isInitDashboard()) { 2928 this.showDashboard(); 2929 } 2930 if (!mobile && window.innerHeight < 760) { 2931 main.find(' > .sb-body').css('max-height', (window.innerHeight - 130) + 'px'); 2932 } 2933 } 2934 this.initialized = true; 2935 if (!admin) { 2936 if (activeUser() && !this.registration(true)) { 2937 if (SBF.getURL('conversation')) { 2938 this.openConversation(SBF.getURL('conversation'), () => { 2939 if (SBF.getURL('rating')) { 2940 setTimeout(() => { SBRichMessages.rating(); }, 600); 2941 let agent = SBChat.conversation.getLastUserMessage(false, true); 2942 SBF.ajax({ 2943 function: 'set-rating', 2944 conversation_id: SBF.getURL('conversation'), 2945 agent_id: agent ? agent.get('user_id') : false, 2946 user_id: activeUser().id, 2947 rating: SBF.getURL('rating') 2948 }); 2949 } 2950 }); 2951 } else if (storage('open-conversation')) { 2952 this.openConversation(storage('open-conversation')); 2953 } 2954 } 2955 if (!this.chat_open && ((!mobile && storage('chat-open')) || SBF.getURL('chat') == 'open') || SBF.getURL('conversation')) { 2956 setTimeout(() => { this.start(); }, 500); 2957 } 2958 2959 if (CHAT_SETTINGS.woocommerce_returning_visitor) { 2960 if (storage('returning-visitor') === false) { 2961 SBF.storageTime('returning-visitor'); 2962 } else if (SBF.storageTime('returning-visitor', 24) && !storage('returning-visitor-processed')) { 2963 setTimeout(() => { 2964 SBF.ajax({ 2965 function: 'woocommerce-returning-visitor' 2966 }, () => { 2967 storage('returning-visitor-processed', true); 2968 }); 2969 }, 15000); 2970 } 2971 } 2972 if (CHAT_SETTINGS.timetable_type) { 2973 SBChat.offlineMessage(); 2974 } 2975 if (CHAT_SETTINGS.queue_human_takeover && SBApps.dialogflow.humanTakeoverActive()) { 2976 CHAT_SETTINGS.queue = true; 2977 } 2978 $(window).on('resize', function () { 2979 if (!mobile && window.innerHeight < 760) { 2980 main.find(' > .sb-body').css('max-height', (window.innerHeight - 130) + 'px'); 2981 } 2982 }); 2983 SBApps.dialogflow.flowOnLoad(); 2984 } 2985 if (tickets) { 2986 SBTickets.init(); 2987 } 2988 SBF.event('SBInit'); 2989 } 2990 }, 2991 2992 // Initialize the chat settings and open the chat 2993 start: function () { 2994 if (this.initialized) { 2995 this.populate(); 2996 this.headerAgent(); 2997 this.updateUsersActivity(); 2998 this.startRealTime(); 2999 this.popup(true); 3000 if (this.conversation) { 3001 this.updateNotifications(this.conversation.id); 3002 } 3003 main.sbActive(true); 3004 $('body').addClass('sb-chat-open'); 3005 this.chat_open = true; 3006 if (CHAT_SETTINGS.welcome_trigger == 'open' && !this.registration(true)) { 3007 this.welcome(); 3008 } 3009 SBApps.martfury.privateChat(); 3010 this.calculateLabelDates(); 3011 } 3012 }, 3013 3014 // Open or close the chat 3015 open: function (open = true) { 3016 if (open && !this.chat_open) { 3017 this.start(); 3018 this.chat_open = true; 3019 this.startRealTime(); 3020 main.sbActive(true); 3021 $('body').addClass('sb-chat-open'); 3022 storage('chat-open', true); 3023 if (this.conversation) { 3024 storage('last-open-message', this.conversation.getLastMessage().id); 3025 } 3026 if (mobile) { 3027 history.pushState({ 'chat-open': true }, '', ''); 3028 } 3029 SBF.event('SBChatOpen'); 3030 } else if (!open && this.chat_open) { 3031 main.sbActive(false); 3032 this.stopRealTime(); 3033 this.chat_open = false;
3034 storage('chat-open', false); 3035 $('body').removeClass('sb-chat-open'); 3036 SBF.event('SBChatClose'); 3037 } 3038 }, 3039 3040 // Get a full conversation and display it in the chat 3041 openConversation: function (conversation_id, onSuccess = false) { 3042 activeUser().getFullConversation(conversation_id, (response) => { 3043 if (!response.id || !SBChat.isConversationAllowed(response.get('source'), response.status_code, JSON.stringify(response && response.getLastMessage() ? response.getLastMessage().payload() : {}))) { 3044 storage('open-conversation', ''); 3045 return false; 3046 } 3047 this.setConversation(response); 3048 this.hideDashboard(); 3049 this.populate(); 3050 this.main_header = false; 3051 if (storage('chat-open') && !mobile) { 3052 SBChat.open(); 3053 } 3054 if (storage('queue') == conversation_id) { 3055 this.queue(conversation_id); 3056 } 3057 if (this.chat_open || tickets) { 3058 this.updateNotifications(conversation_id); 3059 } 3060 if (tickets) { 3061 SBTickets.activateConversation(response); 3062 } 3063 if (onSuccess) { 3064 onSuccess(response); 3065 } 3066 storage('open-conversation', conversation_id); 3067 SBF.event('SBConversationOpen', response); 3068 }); 3069 }, 3070 3071 // Update the active conversation with the latest messages 3072 update: function () { 3073 if (this.conversation) { 3074 if (this.is_busy_update) return; 3075 let last_message = this.conversation.getLastMessage(true); 3076 let is_update = false; 3077 SBF.ajax({ 3078 function: 'get-new-messages', 3079 conversation_id: this.conversation.id, 3080 datetime: this.datetime_last_message_conversation, 3081 last_id: this.id_last_message_conversation 3082 }, (response) => { 3083 let count = response.length; 3084 this.is_busy_update = false; 3085 if (this.conversation) { 3086 if (Array.isArray(response) && count > 0 && (!last_message || last_message.id != response[count - 1].id || last_message.message != response[count - 1].message || last_message.payload != response[count - 1].payload || last_message.attachments != response[count - 1].attachments)) { 3087 let code = ''; 3088 let messages = []; 3089 let id_check = []; 3090 let is_activation = false; 3091 3092 // Generate and add the new messages 3093 this.calculateLabelDateFirst(); 3094 for (var i = 0; i < count; i++) { 3095 if (!id_check.includes(response[i].id) && (!admin || this.conversation.id == response[i].conversation_id)) { 3096 let message = new SBMessage(response[i]); 3097 let payload = message.payload(); 3098 this.id_last_message_conversation = message.id; 3099 this.datetime_last_message_conversation = message.get('creation_time'); 3100 3101 // Payload 3102 if (payload.event) { 3103 let event = payload.event; 3104 if ((event == 'delete-message' && this.conversation.getMessage(message.id) !== false) || (!admin && !message.message && !message.attachments.length && !payload)) { 3105 this.deleteMessage(message.id); 3106 } 3107 if (event == 'woocommerce-update-cart' && !admin) {
3108 SBApps.woocommerce.updateCart(payload.action, payload.id); 3109 } 3110 if (event == 'woocommerce-checkout' && !admin) { 3111 SBApps.wordpress.ajax('url', { url_name: 'checkout' }, (response) => { 3112 setTimeout(() => document.location = response, 500); 3113 }); 3114 } 3115 if (!SBApps.dialogflow.active() && (event == 'conversation-status-update-3' || event == 'conversation-status-update-4' || event == 'activate-bot')) { 3116 SBApps.dialogflow.active('activate'); 3117 is_activation = true; 3118 } 3119 if (event == 'conversation-status-update-3') { 3120 this.conversationArchived(); 3121 } 3122 } 3123 if (payload['human-takeover'] && CHAT_SETTINGS.queue_human_takeover) { 3124 CHAT_SETTINGS.queue = true; 3125 SBChat.queue(SBChat.conversation.id); 3126 } 3127 if (payload['human-takeover-fallback']) { 3128 SBApps.dialogflow.typing_enabled = false; 3129 } 3130 3131 // Message 3132 if (this.conversation.getMessage(response[i].id)) { 3133 this.conversation.updateMessage(message.id, message); 3134 chat.find(`[data-id="${message.id}"]`).replaceWith(message.getCode()); 3135 is_update = true; 3136 } else { 3137 if (message.message || message.attachments.length) { 3138 chat.find(`[data-id="sending"]`).remove(); 3139 } 3140 if (this.conversation.id == response[i].conversation_id) { 3141 this.conversation.addMessages(message); 3142 code += message.getCode(); 3143 is_update = false; 3144 } 3145 } 3146 this.conversation.updateMessagesStatus(); 3147 messages.push(message); 3148 id_check.push(message.id); 3149 if (this.chat_open) { 3150 storage('last-open-message', message.id); 3151 } 3152 if (!admin && ((this.dashboard || !this.chat_open || !this.tab_active) && (message.get('user_id') != activeUser().id) && (message.message || message.attachments.length))) { 3153 this.updateNotifications(this.conversation.id, message.id); 3154 } 3155 } 3156 } 3157 chat.append(code); 3158 3159 // Update status code 3160 let last_message = this.conversation.getLastMessage(); 3161 let user_type = last_message ? last_message.get('user_type') : false; 3162 let is_agent = SBF.isAgent(user_type); 3163 let is_agent_human = is_agent && user_type != 'bot'; 3164 if (!admin && is_agent_human) { 3165 if (this.chat_open) { 3166 if (last_message && !last_message.message.includes('sb-rich-success') && (!response[0].payload || !response[0].payload.includes('conversation-status-update'))) { 3167 this.setConversationStatus(0); 3168 } 3169 if (CHAT_SETTINGS.follow) { 3170 clearTimeout(timeout); 3171 } 3172 } 3173 if (!is_activation) { 3174 SBApps.dialogflow.active(false); 3175 } 3176 } 3177 3178 // Queue 3179 if (storage('queue') == this.conversation.id && is_agent_human) { 3180 this.queue('clear'); 3181 } 3182 3183 // Flash notifications 3184 if (messages.length && (!(SBF.null(messages[0].message) && SBF.null(messages[0].attachments)) || count != 1)) { 3185 if (!admin && !this.tab_active) { 3186 this.flashNotification(); 3187 } 3188 3189 // Sound notifications 3190 if (this.audio && ((!admin && is_agent && CHAT_SETTINGS.sound) || (admin && !is_agent && SB_ADMIN_SETTINGS.sound))) { 3191 this.playSound(); 3192 } 3193 } 3194 3195 // Miscellaneous 3196 this.headerAgent(); 3197 if (!is_update && !this.dashboard) { 3198 this.scrollBottom(); 3199 setTimeout(() => { this.scrollBottom() }, 300); 3200 } 3201 if (CHAT_SETTINGS.auto_open && (this.dashboard || !this.chat_open)) { 3202 this.open(); 3203 } 3204 if (is_agent_human) { 3205 this.typing(-1, 'stop'); 3206 } 3207 this.busy(false); 3208 SBF.event('SBNewMessagesReceived', { messages: messages, conversation_id: this.conversation.id }); 3209 if (tickets) { 3210 SBTickets.onNewMessageReceived(messages[0], this.conversation.id); 3211 } 3212 } 3213 } 3214 }); 3215 this.is_busy_update = true; 3216 setTimeout(() => { this.is_busy_update = false }, 5000); 3217 } else { 3218 this.updateConversations(); 3219 } 3220 }, 3221 3222 // Update the user conversations list with the latest conversations and messages 3223 updateConversations: function () { 3224 if (activeUser()) { 3225 SBF.ajax({ 3226 function: 'get-new-user-conversations', 3227 datetime: this.id_last_message 3228 }, (response) => { 3229 if (response.length) { 3230 this.id_last_message = response[0].message_id; 3231 if (this.chat_open) { 3232 storage('last-open-message', this.id_last_message); 3233 } 3234 for (var i = 0; i < response.length; i++) { 3235 let status_code = response[i].conversation_status_code; 3236 if (!SBChat.isConversationAllowed(response[i].source, status_code, response[i].payload)) { 3237 continue; 3238 } 3239 let conversation_id = response[i].conversation_id; 3240 let message = new SBMessage(response[i]); 3241 let conversation = new SBConversation([message], response[i]); 3242 let is_new = activeUser().addConversation(conversation); 3243 3244 // Red notifications 3245 if (response[i].message_user_id != activeUser().id && (this.conversation.id != conversation_id || !this.chat_open) && (message.message || message.attachments.length)) { 3246 this.updateNotifications(conversation_id, message.id); 3247 if (CHAT_SETTINGS.auto_open) { 3248 this.open(); 3249 } 3250 } 3251 3252 // Payload 3253 let payload = message.payload(); 3254 if (typeof payload !== 'boolean' && payload.event) { 3255 let event = payload.event; 3256 if (event == 'open-chat') { 3257 if (this.conversation.id != conversation_id || this.dashboard) { 3258 this.openConversation(conversation_id); 3259 } 3260 if (!mobile) { 3261 setTimeout(() => { this.open() }, 500); 3262 } 3263 } 3264 if (!message.message && !message.attachments.length) { 3265 continue; 3266 } 3267 } 3268 3269 if (!this.tab_active) { 3270 3271 // Desktop notifications 3272 if (this.desktop_notifications) { 3273 SBChat.desktopNotification(message.get('full_name'), message.message, message.get('profile_image')); 3274 } 3275 3276 // Flash notifications 3277 if (this.flash_notifications) { 3278 this.flashNotification(); 3279 } 3280 3281 // Sound notifications 3282 if (!admin && this.audio && CHAT_SETTINGS.sound && (!this.chat_open || this.dashboard || this.conversation.id != conversation_id) && !(SBF.null(message.message) && SBF.null(message.attachments))) { 3283 this.playSound(); 3284 } 3285 } 3286 if (is_new) { 3287 SBF.event('SBNewConversationReceived', conversation); 3288 } 3289 if (tickets) { 3290 SBTickets.onConversationReceived(conversation); 3291 } 3292 if (!this.conversation && is_new) { 3293 this.openConversation(conversation.id); 3294 } 3295 } 3296 if (this.conversation) { 3297 this.conversation.updateMessagesStatus(); 3298 } 3299 main.find('.sb-user-conversations').html(activeUser().getConversationsCode()); 3300 main.find('.sb-dashboard-conversations').setClass('sb-conversations-hidden', main.find('.sb-user-conversations > li').length > 3); 3301 } 3302 }); 3303 } 3304 }, 3305 3306 // Generate the conversation code and display it 3307 populate: function () { 3308 if (this.conversation) { 3309 let code = ''; 3310 let notify = chat.find(' > .sb-notify-message'); 3311 let last_date = false;
3312 let conversation_id = this.conversation.id; 3313 for (var i = 0; i < this.conversation.messages.length; i++) { 3314 let message = this.conversation.messages[i]; 3315 let current_date = SBF.beautifyTime(message.get('creation_time')); 3316 if (current_date.includes('today')) { 3317 current_date = `<span>${sb_('Today')}</span>`; 3318 } 3319 if (current_date != last_date && (message.message || message.attachments.length || (admin && ['terminate_call', 'incoming_call', 'call_permission_request', 'call_response', 'conversation-department-update', 'conversation-agent-update', 'conversation-status-update'].includes(message.payload('event'))))) { 3320 code += `<div class="sb-label-date">${current_date}</div>`; 3321 last_date = current_date; 3322 } 3323 code += message.getCode(); 3324 } 3325 chat.html((notify.length ? notify[0].outerHTML : '') + code); 3326 if (!this.dashboard) { 3327 this.scrollBottom(); 3328 this.calculateLabelDates(); 3329 if ((admin && SB_ADMIN_SETTINGS.notify_email_cron) || (!admin && CHAT_SETTINGS.notify_email_cron)) { 3330 let last_message = this.conversation.getLastUserMessage(false, !admin); 3331 if (last_message && last_message.id != storage('email-cron-' + conversation_id)) { 3332 SBF.ajax({ function: 'remove-email-cron', conversation_id: conversation_id }); 3333 storage('email-cron-' + conversation_id, last_message.id); 3334 } 3335 } 3336 } 3337 } else if (activeUser() && !activeUser().isConversationsEmpty()) { 3338 if (CHAT_SETTINGS.disable_dashboard) { 3339 this.openConversation(activeUser().conversations[0].id); 3340 } else { 3341 this.showDashboard(); 3342 } 3343 } 3344 }, 3345 3346 // Populate the dashboard with all conversations 3347 populateConversations: function (onSuccess = false) { 3348 if (!this.is_busy_populate && activeUser()) { 3349 this.is_busy_populate = true; 3350 setTimeout(() => { this.is_busy_populate = false }, 5000); 3351 activeUser().getConversations((response) => { 3352 let count = response.length; 3353 let converstion_ids = []; 3354 if (count) { 3355 let now = Date.now(); 3356 let last_message = response[0].messages[0]; 3357 this.id_last_message = last_message.id; 3358 for (var i = 0; i < count; i++) { 3359 converstion_ids.push(response[i].id); 3360 if (!tickets && (response[i].status_code == 1 && storage('last-open-message') < last_message.id) && (!this.conversation || this.conversation.id != response[i].id)) { 3361 this.updateNotifications(response[i].id, last_message.id); 3362 } 3363 if (!mobile && (now - SBF.UTC(response[i].messages[0].get('creation_time'))) < 6000) { 3364 this.open(); 3365 } 3366 } 3367 main.find('.sb-user-conversations').html(activeUser().getConversationsCode()); 3368 main.find('.sb-dashboard-conversations').setClass('sb-conversations-hidden', main.find('.sb-user-conversations > li').length > 3); 3369 } 3370 main.setClass('sb-no-conversations', !count); 3371 if ((!this.initialized || force_action == 'open-conversation') && count == 1 && !this.isInitDashboard() && !storage('open-conversation')) { 3372 this.openConversation(activeUser().getLastConversation().id); 3373 if (force_action == 'open-conversation') { 3374 force_action = ''; 3375 } 3376 } 3377 for (var i = 0; i < SBChat.notifications.length; i++) { 3378 this.updateNotifications(SBChat.notifications[i][0], converstion_ids.includes(SBChat.notifications[i][0]) ? SBChat.notifications[i][1] : false); 3379 } 3380 if (onSuccess) { 3381 onSuccess(response); 3382 } 3383 this.finalizeInit(); 3384 SBF.event('SBPopulateConversations', { conversations: response }); 3385 }); 3386 } 3387 }, 3388 3389 // Create a new conversation and optionally send the first message 3390 newConversation: function (status_code, user_id = - 1, message = '', attachments = [], department = null, agent_id = null, onSuccess = false) { 3391 if (activeUser()) { 3392 SBF.ajax({ 3393 function: 'new-conversation', 3394 status_code: status_code, 3395 title: tickets ? main.find('.sb-ticket-title input').val() : null, 3396 department: SBF.null(department) ? this.default_department : department, 3397 agent_id: SBF.null(agent_id) ? this.default_agent : agent_id, 3398 tags: this.default_tags, 3399 source: tickets ? 'tk' : '' 3400 }, (response) => { 3401 if (SBF.errorValidation(response, 'user-not-found')) { 3402 this.addUserAndLogin(() => { 3403 this.newConversation(status_code, user_id, message, attachments, department, agent_id, onSuccess); 3404 }); 3405 return; 3406 } 3407 let conversation = new SBConversation([], response.details); 3408 this.setConversation(conversation); 3409 if (message || attachments.length) { 3410 this.sendMessage(user_id, message, attachments); 3411 } 3412 if (user_id != bot_id) { 3413 setTimeout(() => { this.queue(conversation.id) }, 1000); 3414 } 3415 if (onSuccess) { 3416 onSuccess(conversation); 3417 } 3418 }); 3419 3420 } else { 3421 SBF.error('activeUser() not setted', 'SBChat.newConversation'); 3422 } 3423 }, 3424 3425 // Set an existing conversation as active conversation 3426 setConversation: function (conversation) { 3427 if (conversation instanceof SBConversation) { 3428 let conversations = activeUser().conversations; 3429 let is_new = true; 3430 this.conversation = conversation; 3431 this.id_last_message_conversation = !this.conversation.getLastMessage(true) ? 0 : this.conversation.getLastMessage(true).id; 3432 this.datetime_last_message_conversation = this.conversation.getLastMessage(true) == false ? '2000-01-01 00:00:00' : this.conversation.getLastMessage(true).get('creation_time'); 3433 if (conversation.id != this.conversation.id) { 3434 this.queue(conversation.id); 3435 } 3436 for (var i = 0; i < conversations.length; i++) { 3437 if (conversations[i].id == conversation.id) { 3438 conversations[i] = conversation; 3439 is_new = false;
3440 break; 3441 } 3442 } 3443 if (is_new) { 3444 conversations.push(conversation); 3445 } 3446 storage('open-conversation', conversation.id); 3447 SBApps.dialogflow.typing_enabled = true; 3448 this.headerAgent(); 3449 SBF.event('SBActiveConversationChanged', conversation); 3450 } else { 3451 SBF.error('Value not of type SBConversation', 'SBChat.setConversation'); 3452 } 3453 }, 3454 3455 // Manage all the queue functionalities 3456 queue: function (conversation_id) { 3457 if (conversation_id == 'clear') { 3458 main.removeClass('sb-notify-active sb-queue-active'); 3459 chat.find(' > .sb-notify-message').remove(); 3460 clearInterval(this.queue_interval); 3461 this.queue_interval = false; 3462 storage('queue', ''); 3463 if (CHAT_SETTINGS.queue_sound && !SBChat.tab_active) { 3464 SBChat.playSound(999); 3465 } 3466 return; 3467 } 3468 if (!admin && CHAT_SETTINGS.queue) { 3469 SBF.ajax({ 3470 function: 'queue', 3471 conversation_id: conversation_id, 3472 department: this.conversation.get('department') 3473 }, (response) => { 3474 chat.find(' > .sb-notify-message').remove(); 3475 let position = response[0]; 3476 if (position == 0) { 3477 this.queue('clear'); 3478 } else { 3479 let time = (!CHAT_SETTINGS.queue_response_time ? 5 : parseInt(CHAT_SETTINGS.queue_response_time)) * position; 3480 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>' + position + '</b>').replace('{minutes}', '<b>' + time + '</b>'); 3481 if (response[1]) { 3482 chat.prepend(`<div class="sb-notify-message sb-rich-cnt"><div class="sb-cnt"><div class="sb-message">${text}</div></div></div>`); 3483 } 3484 if (this.queue_interval === false) { 3485 this.queue_interval = setInterval(() => { this.queue(conversation_id) }, 10100); 3486 if (response[1]) { 3487 main.addClass('sb-notify-active sb-queue-active'); 3488 } 3489 storage('queue', conversation_id); 3490 } 3491 } 3492 SBF.event('SBQueueUpdate', position); 3493 }); 3494 } 3495 }, 3496 3497 // Get the departments details and generate the department code 3498 getDepartmentCode(department_id, onSuccess) { 3499 if (this.departments) { 3500 if (department_id == 'all') { 3501 let code = ''; 3502 for (var key in this.departments) { 3503 this.getDepartmentCode(this.departments[key].id, (response) => { code += response; }); 3504 } 3505 onSuccess(code); 3506 } else { 3507 onSuccess(`<div data-color="${this.departments[department_id].color}">${this.departments[department_id].image ? `<img loading="lazy" src="${this.departments[department_id].image}" />` : ''}<div>${this.departments[department_id].name}<div></div>`); 3508 } 3509 } else { 3510 SBF.ajax({ 3511 function: 'get-departments' 3512 }, (response) => { 3513 if (response) { 3514 this.departments = response; 3515 this.getDepartmentCode(department_id, onSuccess); 3516 } 3517 }); 3518 } 3519 }, 3520 3521 // Start and stop the real time check of new messages 3522 startRealTime: function () { 3523 if (SBPusher.active) return; 3524 this.stopRealTime(); 3525 this.real_time = setInterval(() => { 3526 this.update(); 3527 this.typing(admin ? (activeUser() ? activeUser().id : -1) : this.agent_id, 'check'); 3528 }, 1000); 3529 }, 3530 3531 stopRealTime: function () { 3532 clearInterval(this.real_time); 3533 }, 3534 3535 // Check if the agent is online and set the online status of the active user 3536 updateUsersActivity: function (presence_add_user_id = false) { 3537 SBF.updateUsersActivity(activeUser() ? activeUser().id : false, this.agent_id, (response) => { 3538 if (!this.typing_settings.typing) { 3539 if (response == 'online' || this.agent_id == bot_id || ((!this.conversation || (this.conversation && !this.dashboard)) && this.agent_id == -1 && (agents_online || (presence_add_user_id && (!activeUser() || presence_add_user_id != activeUser().id))))) { 3540 $(chat_status).addClass('sb-status-online').html(sb_('Online')); 3541 this.agent_online = this.agent_id != bot_id; 3542 } else { 3543 $(chat_status).removeClass('sb-status-online').html(sb_('Away')); 3544 this.agent_online = false;
3545 } 3546 } 3547 }); 3548 }, 3549 3550 // Show the loading icon and put the chat in busy mode 3551 busy: function (value) { 3552 if (chat_editor) { 3553 chat_editor.find('.sb-loader').sbActive(value); 3554 } 3555 this.is_busy = value; 3556 SBF.event('SBBusy', value); 3557 }, 3558 3559 // Manage the agent header 3560 headerAgent: function (is_default_chatbot = false) { 3561 if (!admin && !tickets && !this.dashboard && (this.agent_id == -1 || (this.conversation && this.conversation.getLastMessage() && SBF.isAgent(this.conversation.getLastMessage().get('user_type')) && this.conversation.getLastMessage().get('user_id') != this.agent_id))) { 3562 let agent = this.lastAgent(false); 3563 let is_continue = true; 3564 agent = agent && SBApps.dialogflow.humanTakeoverActive() && SBPusher.active && !SBPusher.presenceCheck({ info: { user_type: 'agent' }, id: agent.user_id }) ? agent : this.lastAgent(); 3565 if (!agent && (!this.conversation || (this.conversation && !this.dashboard))) { 3566 if (CHAT_SETTINGS.is_chatbot_active) { 3567 is_default_chatbot = true; 3568 } else { 3569 is_continue = !chat_header.hasClass('sb-header-agent'); 3570 if (is_continue) { 3571 agent = chat_header.hasClass('sb-header-type-agents') ? { user_id: chat_header.find('.sb-profiles > div:first-child').attr('data-id'), full_name: chat_header.find('.sb-profiles > div:first-child > span').text().trim(), profile_image: chat_header.find('img').eq(0).attr('src') } : { user_id: -1, full_name: chat_header.find('.sb-title').text().trim(), profile_image: chat_header.find('img').attr('src') }; 3572 } 3573 } 3574 } 3575 if (!agent && is_default_chatbot) { 3576 agent = { user_id: CHAT_SETTINGS.bot_id, full_name: CHAT_SETTINGS.bot_name, profile_image: CHAT_SETTINGS.bot_image }; 3577 } 3578 if (agent) { 3579 let replace_agent_animation = this.agent_id != agent.user_id && chat_header.hasClass('sb-header-agent'); 3580 this.headerReset(); 3581 this.agent_id = agent.user_id; 3582 chat_header.addClass('sb-header-agent').attr('data-agent-id', this.agent_id); 3583 if (replace_agent_animation) { 3584 chat_header.find('.sb-profile').addClass('sb-replace-agent-animation'); 3585 setTimeout(() => { 3586 chat_header.find('img').attr('src', agent['profile_image']); 3587 chat_header.find('.sb-name').html(agent['full_name']); 3588 }, 500); 3589 } else { 3590 chat_header.html(`<div class="sb-dashboard-btn sb-icon-arrow-left"></div><div class="sb-profile">${agent.profile_image ? `<img loading="lazy" src="${agent.profile_image}" />` : ''}<div><span class="sb-name">${agent.full_name}</span><span class="sb-status">${sb_('Away')}</span></div><i class="sb-icon sb-icon-close ${CHAT_SETTINGS.close_chat ? 'sb-close-chat' : 'sb-responsive-close-btn'}"></i>${mobile ? '' : '<i class="sb-icon sb-icon-arrows-' + (main.hasClass('sb-chat-large') ? 'in' : 'out') + ' sb-enlarge-chat-widget"></i>'}</div><div class="sb-label-date-top"></div>`); 3591 } 3592 chat_status = chat_header.find('.sb-status'); 3593 this.updateUsersActivity(); 3594 this.label_date = chat_header.find('.sb-label-date-top'); 3595 if (SBF.storageTime('header-animation', 1)) { 3596 this.headerAnimation(); 3597 } 3598 } else if (is_continue) { 3599 this.headerReset(); 3600 chat_header.html(this.start_header[0]).addClass(this.start_header[1]); 3601 } 3602 } 3603 }, 3604 3605 headerReset: function () { 3606 if (this.start_header == false) { 3607 this.start_header = [chat_header.html(), chat_header.attr('class')]; 3608 } 3609 chat_header.removeClass('sb-header-main sb-header-brand sb-header-agent sb-header-minimal'); 3610 this.main_header = false;
3611 }, 3612 3613 headerAnimation: function () { 3614 chat_header.addClass('sb-header-animation'); 3615 setTimeout(() => { chat_header.removeClass('sb-header-animation') }, 8000); 3616 SBF.storageTime('header-animation'); 3617 }, 3618 3619 // Return the last agent of the active conversation 3620 lastAgent: function (bot = true) { 3621 let agent = false; 3622 if (this.conversation) { 3623 let message = this.conversation.getLastUserMessage(false, bot ? 'all' : true); 3624 if (message) { 3625 agent = { user_id: message.get('user_id'), full_name: message.get('full_name'), profile_image: message.get('profile_image') }; 3626 } 3627 } 3628 return agent; 3629 }, 3630 3631 // Scroll the chat to the bottom 3632 scrollBottom: function (top = false) { 3633 setTimeout(() => { 3634 chat_scroll_area.scrollTop(top ? 0 : chat_scroll_area[0].scrollHeight); 3635 this.scrollHeader(); 3636 }, 20); 3637 }, 3638 3639 // Check if the chat is at bottom 3640 isBottom: function () { 3641 return chat_scroll_area[0].scrollTop === (chat_scroll_area[0].scrollHeight - chat_scroll_area[0].offsetHeight); 3642 }, 3643 3644 // Enlarge or shrink the chat widget 3645 resize: function (is_enlarge = true) { 3646 let height = window.innerHeight - 130; 3647 let button = chat_header.find('.sb-enlarge-chat-widget'); 3648 main.setClass('sb-chat-large', is_enlarge); 3649 main.find(' > .sb-body').css('max-height', (is_enlarge ? height : (height > 650 ? 650 : height)) + 'px'); 3650 button.removeClass('sb-icon-arrows-in sb-icon-arrows-out'); 3651 button.addClass(is_enlarge ? 'sb-icon-arrows-in' : 'sb-icon-arrows-out'); 3652 }, 3653 3654 // Dashboard header animation 3655 scrollHeader: function () { 3656 if (this.main_header && this.dashboard) { 3657 let scroll = chat_scroll_area.scrollTop(); 3658 if (scroll > -1 && scroll < 1000) { 3659 chat_header.find('.sb-content').css({ 'opacity': (1 - (scroll / 500)), 'top': (scroll / 10 * -1) + 'px' }); 3660 }; 3661 } 3662 }, 3663 3664 // Display the dashboard area 3665 showDashboard: function () { 3666 if (!admin && !tickets) { 3667 main.addClass('sb-dashboard-active'); 3668 chat_header.removeClass('sb-header-agent'); 3669 this.hidePanel() 3670 if (this.start_header) { 3671 chat_header.html(this.start_header[0]).addClass(this.start_header[1]); 3672 } 3673 chat_scroll_area.find(' > div').sbActive(false); 3674 main.find('.sb-dashboard').sbActive(true); 3675 this.populateConversations(); 3676 this.conversation = false; 3677 this.agent_id = -1; 3678 this.stopRealTime(); 3679 this.dashboard = true; 3680 this.main_header = true; 3681 this.scrollBottom(true); 3682 this.resize(false); 3683 SBF.event('SBDashboard'); 3684 } 3685 }, 3686 3687 // Hide the dashboard area 3688 hideDashboard: function () { 3689 if (!admin && !tickets) { 3690 chat.sbActive(true); 3691 main.removeClass('sb-dashboard-active').find('.sb-dashboard').sbActive(false); 3692 this.dashboard = false; 3693 this.headerAgent(); 3694 this.scrollHeader(0); 3695 if (this.chat_open) { 3696 this.startRealTime(); 3697 } 3698 SBF.event('SBDashboardClosed'); 3699 } 3700 }, 3701 3702 // Show a chat panel 3703 showPanel: function (name, title) { 3704 if (tickets) { 3705 return SBTickets.showPanel(name, title); 3706 } 3707 let panel = chat_scroll_area.find(' > .sb-panel-' + name); 3708 if (panel.length) { 3709 chat_scroll_area.find(' > div').sbActive(false); 3710 panel.sbActive(true); 3711 if (!this.start_header) { 3712 this.start_header = [chat_header.html(), chat_header.attr('class')]; 3713 } 3714 chat_header.attr('class', 'sb-header sb-header-panel').html(`<span>${sb_(title)}</span>${mobile ? '' : '<i class="sb-icon sb-icon-arrows-' + (main.hasClass('sb-chat-large') ? 'in' : 'out') + ' sb-enlarge-chat-widget sb-enlarge-chat-widget-panel"></i>'}<div class="sb-dashboard-btn sb-icon-close"></div>`); 3715 main.addClass('sb-panel-active'); 3716 this.dashboard = true; 3717 } 3718 SBF.event('SBPanelActive', name); 3719 }, 3720 3721 hidePanel: function () { 3722 main.removeClass('sb-panel-active'); 3723 chat_header.removeClass('sb-header-panel'); 3724 }, 3725 3726 // Clear the conversation area and the active conversation 3727 clear: function () { 3728 this.conversation = false;
3729 chat.html(''); 3730 }, 3731 3732 // Update the red notification counter of the chat 3733 updateNotifications: function (conversation_id, message_id = false) { 3734 let check = false; 3735 if (message_id) { 3736 for (var i = 0; i < this.notifications.length; i++) { 3737 if (this.notifications[i][0] == conversation_id && message_id == this.notifications[i][1]) { 3738 check = true; 3739 } 3740 } 3741 if (!check) { 3742 this.notifications.push([conversation_id, message_id]); 3743 if (!this.dashboard && this.conversation && this.conversation.id != conversation_id) { 3744 this.headerAnimation(); 3745 } 3746 } 3747 } else { 3748 let active_notifications = []; 3749 for (var i = 0; i < this.notifications.length; i++) { 3750 if (this.notifications[i][0] == conversation_id) { 3751 check = true; 3752 } else { 3753 active_notifications.push(this.notifications[i]); 3754 } 3755 } 3756 if (!admin && check && ['0', 0].includes(this.conversation.status_code)) { 3757 this.setConversationStatus(1, true); 3758 } 3759 this.notifications = active_notifications; 3760 } 3761 let count = this.notifications.length; 3762 storage('notifications', this.notifications); 3763 main.find('.sb-chat-btn span').attr('data-count', count).html(count > -1 ? count : 0); 3764 SBF.event('SBNotificationsUpdate', { conversation_id: conversation_id, message_id: message_id }); 3765 }, 3766 3767 // Set the active conversation status 3768 setConversationStatus: function (status_code, prevent_2_to_1 = false) { 3769 if (this.conversation) { 3770 SBF.ajax({ 3771 function: 'update-conversation-status', 3772 conversation_id: this.conversation.id, 3773 status_code: status_code, 3774 prevent_2_to_1: prevent_2_to_1 3775 }, () => { 3776 this.conversation.set('status_code', status_code); 3777 SBF.event('SBActiveConversationStatusUpdated', { conversation_id: this.conversation.id, status_code: status_code }); 3778 }); 3779 return true; 3780 } 3781 return false; 3782 }, 3783 3784 // Typing status 3785 typing: function (user_id = -1, action = 'check') { 3786 if (this.conversation) { 3787 let valid = this.agent_online || (admin && this.user_online); 3788 if (action == 'check' && !SBPusher.active && user_id != -1 && user_id != bot_id && valid) { 3789 SBF.ajax({ 3790 function: 'is-typing', 3791 user_id: user_id, 3792 conversation_id: this.conversation.id 3793 }, (response) => { 3794 if (response && !this.typing_settings.typing) { 3795 this.typing(-1, 'start'); 3796 } else if (!response && this.typing_settings.typing) { 3797 this.typing(-1, 'stop'); 3798 } 3799 }); 3800 } else if (action == 'set' && valid) { 3801 let source = this.conversation.get('source'); 3802 clearTimeout(timeout_typing); 3803 if (source) { 3804 source = source == 'fb' ? [source, activeUser().getExtra('facebook-id').value, this.conversation.get('extra')] : (source == 'tw' ? [source, activeUser().getExtra('twitter-id').value] : false); 3805 } 3806 if (SBPusher.active) { 3807 SBF.debounce(() => { 3808 SBPusher.trigger('client-typing', { user_id: admin ? SB_ACTIVE_AGENT.id : activeUser().id, conversation_id: this.conversation.id }); 3809 if (source) { 3810 SBF.ajax({ function: 'set-typing', source: source }); 3811 } 3812 }, '#2'); 3813 } else { 3814 if (!this.typing_settings.sent) { 3815 this.typing_settings.sent = true; 3816 SBF.ajax({ 3817 function: 'set-typing', 3818 user_id: user_id, 3819 conversation_id: this.conversation.id, 3820 source: source 3821 }); 3822 this.typing(user_id, 'set'); 3823 } else { 3824 clearTimeout(this.typing_settings.timeout); 3825 this.typing_settings.timeout = setTimeout(() => { 3826 SBF.ajax({ 3827 function: 'set-typing', 3828 user_id: user_id, 3829 conversation_id: -1 3830 }, () => { 3831 this.typing_settings.sent = false;
3832 }); 3833 }, 2000); 3834 } 3835 } 3836 } else if (action == 'start' || action == 'stop') { 3837 let start = action == 'start'; 3838 if (!admin && chat_status) { 3839 if (start) { 3840 let agent = this.lastAgent(); 3841 $(chat_status).addClass('sb-status-typing').html(sb_('Typing')); 3842 if (!agent && CHAT_SETTINGS.is_chatbot_active) { 3843 agent = { user_id: CHAT_SETTINGS.bot_id, full_name: CHAT_SETTINGS.bot_name, profile_image: CHAT_SETTINGS.bot_image }; 3844 } 3845 if (agent && !chat.find('[data-id="typing"]').length) { 3846 chat.append((new SBMessage({ id: 'typing', profile_image: agent.profile_image, full_name: agent.full_name, user_type: 'agent', creation_time: '0000-00-00 00:00:00', message: ' ' })).getCode()); 3847 this.scrollBottom(); 3848 } 3849 } else { 3850 let online = this.agent_online || this.agent_id == bot_id; 3851 clearTimeout(timeout_typing); 3852 $(chat_status).removeClass('sb-status-typing').html(sb_(online ? 'Online' : 'Away')); 3853 if (online) { 3854 $(chat_status).addClass('sb-status-online'); 3855 } 3856 chat.find('[data-id="typing"]').remove(); 3857 } 3858 } 3859 this.typing_settings.typing = start; 3860 SBF.event('SBTyping', start); 3861 } 3862 } 3863 }, 3864 3865 // Articles 3866 showArticles: function (id = false, is_category = false) { 3867 let panel = tickets ? main.find('.sb-panel-main .sb-panel') : chat_scroll_area.find(' > .sb-panel-articles'); 3868 panel.html('').sbLoading(true); 3869 this.showPanel('articles', CHAT_SETTINGS.articles_title ? CHAT_SETTINGS.articles_title : 'Help Center'); 3870 if (!id && CHAT_SETTINGS.articles_categories) { 3871 this.getArticleCategories((categories) => { 3872 this.showArticles_(categories, panel, { categories: categories }); 3873 }, 'parent'); 3874 } else { 3875 this.getArticles(is_category ? false : id, (articles) => { 3876 if (id && !is_category) { 3877 panel.html(this.getArticleCode(articles[0])); 3878 panel.sbLoading(false); 3879 SBF.event('SBArticles', { id: id, articles: articles }); 3880 } else { 3881 this.showArticles_(articles, panel, { id: id, articles: articles }); 3882 } 3883 }, is_category ? id : false); 3884 } 3885 }, 3886 3887 showArticles_: function (items, panel, payload) { 3888 let code = ''; 3889 let language = typeof SB_LANG != ND ? SB_LANG[0] : false; 3890 for (var i = 0; i < items.length; i++) { 3891 let is_article = 'content' in items[i]; 3892 code += `<div data-id="${items[i].id}"${is_article ? '' : ' data-is-category="true"'}><div>${is_article ? items[i].title : (language && items[i].languages && items[i].languages[language] ? items[i].languages[language].title : items[i].title)}</div><span>${is_article ? items[i].content : (language && items[i].languages && items[i].languages[language] ? items[i].languages[language].description : (items[i].description ? items[i].description : ''))}</span></div>`; 3893 } 3894 panel.html(`<div class="sb-articles">${code}</div>`); 3895 panel.sbLoading(false); 3896 SBF.event('SBArticles', payload); 3897 }, 3898 3899 getArticles: function (id = false, onSuccess = false, category = false, count = false) { 3900 SBF.ajax({ 3901 function: 'get-articles', 3902 categories: this.articles_category ? this.articles_category : category, 3903 id: id ? id : this.articles_allowed_ids, 3904 count: count, 3905 return_categories: this.articles_category ? true : false, 3906 full: id, 3907 skip_language: true 3908 }, (response) => { 3909 onSuccess(response); 3910 }); 3911 }, 3912 3913 getArticleCategories: function (onSuccess = false, category_type = false) { 3914 SBF.ajax({ 3915 function: 'get-articles-categories', 3916 category_type: category_type 3917 }, (response) => { 3918 onSuccess(response); 3919 }); 3920 }, 3921 3922 searchArticles: function (search, button, target) { 3923 if (search) { 3924 $(button).sbLoading(true); 3925 SBF.ajax({ 3926 function: 'search-articles', 3927 search: search 3928 }, (articles) => { 3929 let code = ''; 3930 let count = articles.length; 3931 if (count == 0) { 3932 code += `<p class="sb-no-results">${sb_('No articles found.')}</p>`; 3933 } else { 3934 for (var i = 0; i < articles.length; i++) { 3935 code += `<div data-id="${articles[i].id}"><div>${articles[i].title}</div><span>${articles[i].content}</span></div>`; 3936 } 3937 } 3938 $(target).html(code); 3939 $(button).sbLoading(false); 3940 }); 3941 } 3942 }, 3943 3944 setArticleRating: function (article_id, rating, onSuccess = false) { 3945 SBF.ajax({ 3946 function: 'article-ratings', 3947 article_id: article_id, 3948 rating: rating 3949 }, (response) => { 3950 if (onSuccess) onSuccess(response); 3951 }); 3952 }, 3953 3954 articleRatingOnClick: function (button) { 3955 let article = $(button).closest('.sb-article'); 3956 if (!article[0].hasAttribute('data-user-rating')) { 3957 $(button).parent().sbLoading(); 3958 let rating = $(button).attr('data-rating') == 'positive' ? 1 : -1; 3959 let article_id = $(button).closest('.sb-article').attr('data-id'); 3960 SBChat.setArticleRating(article_id, rating, () => { 3961 SBF.storage('article-rating-' + article_id, rating); 3962 article.attr('data-user-rating', rating); 3963 $(button).parent().sbLoading(false); 3964 }); 3965 } 3966 }, 3967 3968 getArticleCode: function (article) {
3969 let user_rating = SBF.storage('article-rating-' + article.id); 3970 let code = ''; 3971 if (this.articles_categories && !SBF.null(article.categories)) { 3972 for (var i = 0; i < this.articles_categories.length; i++) { 3973 let category_id = this.articles_categories[i].id; 3974 if (article.categories.includes(category_id) || article.parent_category == category_id) { 3975 code += `<span data-id="${category_id}">${sb_(this.articles_categories[i].title)}</span>`; 3976 } 3977 } 3978 } 3979 return `<div data-id="${article.id}"${user_rating ? ` data-user-rating="${user_rating}"` : ''} class="sb-article"><div class="sb-title">${article.title}<div class="sb-close sb-icon-close"></div></div><div class="sb-content">${article.content.replace(/(?:\r\n|\r|\n)/g, '<br>')}</div>${article.link ? `<div class="sb-read-more-cnt"><a href="${article.link}" target="_blank" class="sb-btn">${sb_('Read more')}</a></div>` : ''}${code ? `<div class="sb-article-category-links">${code}</div>` : ''}<div class="sb-rating"><span>${sb_('Rate and review')}</span><div><i data-rating="positive" class="sb-submit sb-icon-like"><span>${sb_('Helpful')}</span></i><i data-rating="negative" class="sb-submit sb-icon-dislike"><span>${sb_('Not helpful')}</span></i></div></div></div>`; 3980 }, 3981 3982 initArticlesPage: function () { 3983 let query_article_id = SBF.getURL('article_id'); 3984 let query_category = SBF.getURL('category'); 3985 let query_search = SBF.getURL('search'); 3986 if (CHAT_SETTINGS.articles_url_rewrite) { 3987 let url_parts = location.href.replace(CHAT_SETTINGS.articles_url_rewrite, '').split('/'); 3988 if (url_parts[url_parts.length - 2] == 'category') { 3989 query_category = url_parts[url_parts.length - 1]; 3990 } 3991 if ((url_parts.length == 2 && !url_parts[0] && url_parts[1]) || (url_parts.length == 1 && url_parts[0])) { 3992 query_article_id = url_parts[url_parts.length - 1]; 3993 } 3994 CHAT_SETTINGS.articles_page_url 3995 } 3996 articles_page = $('body').find('#sb-articles'); 3997 if (!articles_page.length) { 3998 articles_page = $('body'); 3999 } 4000 if (articles_page.sbLoading()) { 4001 let url = SB_URL + '/include/articles.php' + (query_category ? '?category=' + query_category : (query_article_id ? '?article_id=' + query_article_id : (query_search ? '?search=' + query_search : ''))); 4002 if (cloud_data) { 4003 url += (url.includes('?') ? '&' : '?') + 'cloud=' + cloud_data; 4004 } 4005 if (SBF.getURL('lang')) { 4006 url += (url.includes('?') ? '&' : '?') + 'lang=' + SBF.getURL('lang'); 4007 } 4008 SBF.loadResource(SB_URL + '/css/articles.css'); 4009 SBF.cors('GET', url, (html) => { 4010 articles_page.html(html); 4011 articles_page.sbLoading(false); 4012 this.initSingleArticlePage(); 4013 }); 4014 } 4015 articles_page.on('keydown', '.sb-panel-side input', function (e) { 4016 if (e.which == 13) { 4017 $(this).next().click(); 4018 } 4019 }); 4020 4021 articles_page.on('click', '.sb-articles > [data-id]', function () { 4022 cache = panel_main.html(); 4023 panel_main.sbLoading(true); 4024 SBChat.getArticles($(this).attr('data-id'), (article) => { 4025 panel_main.removeClass('sb-articles').html(SBChat.getArticleCode(article)); 4026 panel_main.sbLoading(false); 4027 }); 4028 }); 4029 4030 articles_page.on('click', '.sb-article [data-rating]', function () { 4031 SBChat.articleRatingOnClick(this); 4032 }); 4033 4034 articles_page.on('click', '.sb-article .sb-title .sb-close', function () { 4035 articles_page.find('.sb-panel-main').addClass('sb-articles').html(cache); 4036 }); 4037 4038 articles_page.on('click', '.sb-submit-articles', function () { 4039 let search = $(this).parent().find('input').val(); 4040 if (search) { 4041 cache = panel_main.html(); 4042 panel_main.html(''); 4043 SBChat.searchArticles(search, this, panel_main); 4044 } else { 4045 panel_main.html(cache); 4046 } 4047 panel_main.addClass('sb-articles'); 4048 }); 4049 4050 articles_page.on('click', '.sb-article-categories [data-id]', function () { 4051 if (loading(panel_main)) return; 4052 let code = ''; 4053 cache = panel_main.html(); 4054 articles_page.find('.sb-article-categories [data-id]').sbActive(false); 4055 $(this).sbActive(true); 4056 SBChat.getArticles(-1, (articles) => { 4057 for (var i = 0; i < articles.length; i++) { 4058 code += `<div data-id="${articles[i].id}"><div>${articles[i].title}</div><span>${articles[i].content}</span></div>`; 4059 } 4060 panel_main.addClass('sb-articles').html(code ? code : `<p class="sb-no-results">${sb_('No articles found.')}</p>`); 4061 panel_main.sbLoading(false); 4062 }, $(this).attr('data-id')); 4063 }); 4064 }, 4065 4066 initSingleArticlePage: function () { 4067 if (typeof SB_ARTICLE_ID === ND) return;
4068 let user_rating = SBF.storage('article-rating-' + SB_ARTICLE_ID); 4069 const article = $('.sb-article'); 4070 const nav = $('.sb-articles-nav'); 4071 const links = nav.find('a[href^="#"]'); 4072 const sections = []; 4073 const offset = (nav[0].getBoundingClientRect().top + 40) + 'px'; 4074 if (user_rating) { 4075 article.attr('data-user-rating', user_rating); 4076 } 4077 4078 article.on('click', '.sb-rating-ext [data-rating]', function (e) { 4079 SBChat.articleRatingOnClick(this); e.preventDefault(); return false; 4080 }); 4081 4082 nav.on('click', '.sb-articles-nav-mobile-btn', function () { 4083 $(this).parent().toggleClass('sb-active'); 4084 }); 4085 4086 nav.on('click', 'a', function () { 4087 if (window.innerWidth < 768) { 4088 nav.removeClass('sb-active'); 4089 } 4090 history.replaceState(null, '', $(this).attr('href')); 4091 }); 4092 4093 nav.css('--offset', offset); 4094 $('.sb-articles-list-nav').css('--offset', offset); 4095 links.eq(0).addClass('sb-active'); 4096 links.each((i, e) => { 4097 const section = article.find('#' + $(e).attr('href').slice(1)); 4098 if (section.length) { 4099 sections.push({ link: $(e), section: section[0] }); 4100 } 4101 }); 4102 const observer = new IntersectionObserver((entries) => { 4103 entries.forEach(entry => { 4104 if (entry.isIntersecting) { 4105 links.removeClass('sb-active'); 4106 const current = sections.find(s => s.section === entry.target); 4107 if (current) { 4108 current.link.addClass('sb-active'); 4109 } 4110 } 4111 }); 4112 }, { rootMargin: '-40% 0px -50% 0px', threshold: 0 }); 4113 sections.forEach(s => observer.observe(s.section)); 4114 links.each((i, e) => { 4115 $(e).on('click', function (event) { 4116 event.preventDefault(); 4117 const target = article.find('#' + $(this).attr('href').slice(1)); 4118 if (target.length) { 4119 window.scrollTo({ top: target[0].offsetTop - 80, behavior: 'smooth' }); 4120 } 4121 }); 4122 }); 4123 4124 }, 4125 4126 // Emoji 4127 categoryEmoji: function (category) { 4128 let list = this.emoji_options.list; 4129 if (category == 'all') { 4130 this.emoji_options.list_now = list; 4131 } else { 4132 this.emoji_options.list_now = []; 4133 for (var i = 0; i < list.length; i++) { 4134 if (list[i].category.startsWith(category)) { 4135 this.emoji_options.list_now.push(list[i]); 4136 } 4137 } 4138 } 4139 this.emoji_options.range = 0; 4140 this.populateEmoji(0); 4141 this.populateEmojiBar(); 4142 }, 4143 4144 mouseWheelEmoji: function (e) { 4145 let range = this.emoji_options.range; 4146 if (sbDelta(e) > 0 || (mobile && typeof e.originalEvent.changedTouches !== ND && this.emoji_options.touch < e.originalEvent.changedTouches[0].clientY)) { 4147 range -= (range < 1 ? 0 : 1); 4148 } else { 4149 range += (range > this.emoji_options.range_limit ? 0 : 1); 4150 } 4151 chat_emoji.find('.sb-emoji-bar > div').sbActive(false).eq(range).sbActive(true); 4152 this.emoji_options.range = range; 4153 this.populateEmoji(range); 4154 e.preventDefault(); 4155 }, 4156 4157 insertEmoji: function (emoji) { 4158 if (emoji.indexOf('.svg') > 0) { 4159 emoji = $.parseHTML(emoji)[0].alt; 4160 } 4161 this.insertText(emoji); 4162 chat_emoji.sbTogglePopup(); 4163 }, 4164 4165 showEmoji: function (button) { 4166 if (chat_emoji.sbTogglePopup(button)) { 4167 if (!admin) { 4168 chat_emoji.css({ left: chat_editor.offset().left + (tickets ? 68 : 20), top: chat_editor.offset().top - window.scrollY - (tickets ? chat_editor.height() - 330 : 304) }); 4169 } 4170 if (!chat_emoji.find('.sb-emoji-list > ul').html()) { 4171 jQuery.ajax({ 4172 method: 'POST', 4173 url: SB_AJAX_URL, 4174 data: { 4175 function: 'emoji', 4176 'login-cookie': SBF.loginCookie() 4177 } 4178 }).done((response) => { 4179 this.emoji_options.list = JSON.parse(response); 4180 this.emoji_options.list_now = this.emoji_options.list; 4181 this.populateEmoji(0); 4182 this.populateEmojiBar(); 4183 }); 4184 } 4185 SBF.deselectAll(); 4186 } 4187 }, 4188 4189 populateEmoji: function (range) { 4190 let code = ''; 4191 let per_page = mobile ? 42 : 48; 4192 let limit = range * per_page + per_page; 4193 let list_now = this.emoji_options.list_now; 4194 if (limit > list_now.length) limit = list_now.length; 4195 this.emoji_options.range_limit = list_now.length / per_page - 1; 4196 this.emoji_options.range = range; 4197 for (var i = (range * per_page); i < limit; i++) { 4198 code += `<li>${list_now[i].char}</li>`; 4199 } 4200 chat_emoji.find('.sb-emoji-list').html(`<ul>${code}</ul>`); 4201 }, 4202 4203 populateEmojiBar: function () { 4204 let code = '<div class="sb-active"></div>'; 4205 let per_page = mobile ? 42 : 49; 4206 for (var i = 0; i < this.emoji_options.list_now.length / per_page - 1; i++) { 4207 code += '<div></div>'; 4208 } 4209 this.emoji_options.range = 0; 4210 chat_emoji.find('.sb-emoji-bar').html(code); 4211 }, 4212 4213 clickEmojiBar: function (item) { 4214 let range = $(item).index(); 4215 this.populateEmoji(range); 4216 this.emoji_options.range = range; 4217 chat_emoji.find('.sb-emoji-bar > div').sbActive(false).eq(range).sbActive(true); 4218 }, 4219 4220 searchEmoji: function (search) { 4221 SBF.search(search, () => { 4222 if (search.length > 1) { 4223 let list = this.emoji_options.list; 4224 let list_now = []; 4225 for (var i = 0; i < list.length; i++) { 4226 if (list[i].category.toLowerCase().includes(search) || list[i].name.toLowerCase().includes(search)) { 4227 list_now.push(list[i]); 4228 } 4229 } 4230 this.emoji_options.list_now = list_now; 4231 } else { 4232 this.emoji_options.list_now = this.emoji_options.list; 4233 } 4234 this.emoji_options.range = 0; 4235 this.populateEmoji(0); 4236 this.populateEmojiBar(); 4237 }); 4238 }, 4239 4240 // Editor methods 4241 textareaChange: function (textarea) { 4242 let value = $(textarea).val(); 4243 4244 // Saved replies 4245 if (admin) { 4246 SBAdmin.conversations.savedReplies(textarea, value); 4247 SBAdmin.apps.openAI.rewriteButton(value); 4248 } 4249 4250 // Typing 4251 if (value) { 4252 this.typing((admin && !SBPusher.active ? SB_ACTIVE_AGENT.id : activeUser().id), 'set'); 4253 } 4254 chat_editor.sbActive(value); 4255 }, 4256 4257 insertText: function (text) { 4258 let textarea = $(chat_textarea.get(0)); 4259 let index = 0; 4260 if (this.dashboard || !text) return false;
4261 if (textarea.get(0).selectionStart) { 4262 index = textarea.get(0).selectionStart; 4263 } else if (document.selection) { 4264 textarea.focus(); 4265 let selection = document.selection.createRange(); 4266 var selection_length = document.selection.createRange().text.length; 4267 selection.moveStart('character', -textarea.value.length); 4268 index = selection.text.length - selection_length; 4269 } 4270 textarea.val(textarea.val().substr(0, index) + text + textarea.val().substr(index)); 4271 textarea.focus(); 4272 textarea.manualExpandTextarea(); 4273 chat_editor.sbActive(true); 4274 }, 4275 4276 enabledAutoExpand: function () { 4277 if (chat_textarea.length) { 4278 chat_textarea.autoExpandTextarea(); 4279 } 4280 }, 4281 4282 cancelReply: function () { 4283 chat_editor.find('[data-reply]').remove(); 4284 chat.removeClass('sb-reply-active'); 4285 }, 4286 4287 cancelEdit: function () { 4288 chat_editor.find('[data-edit-message]').remove(); 4289 chat.removeClass('sb-edit-message-active'); 4290 }, 4291 4292 // Privacy message 4293 privacy: function () { 4294 SBF.ajax({ 4295 function: 'get-block-setting', 4296 value: 'privacy' 4297 }, (response) => { 4298 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.replace(/\n/g, '<br>')}</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>`); 4299 this.finalizeInit(); 4300 SBF.event('SBPrivacy'); 4301 }); 4302 if (!this.dashboard) { 4303 this.showDashboard(); 4304 } 4305 this.dashboard = true; 4306 main.addClass('sb-init-form-active'); 4307 }, 4308 4309 // Popup message 4310 popup: function (close = false, content = false) { 4311 if (close) { 4312 let popup = main.find('.sb-popup-message'); 4313 let id = popup.attr('data-id'); 4314 storage('popup' + (SBF.null(id) ? '' : id), true); 4315 popup.remove(); 4316 return; 4317 } 4318 setTimeout(() => { 4319 if (!this.chat_open) { 4320 if (content == false) { 4321 content = CHAT_SETTINGS.popup; 4322 } 4323 main.find('.sb-popup-message').remove(); 4324 main.append(`<div data-id="${content.id ? content.id : ''}" class="sb-popup-message">` + (content.image ? `<img loading="lazy" src="${content.image}" />` : '') + (content.title ? `<div class="sb-top">${content.title}</div>` : '') + `<div class="sb-text">${content.message}</div><div class="sb-icon-close"></div></div>`); 4325 SBF.event('SBPopup', content); 4326 } 4327 }, 1000); 4328 }, 4329 4330 // Follow up message 4331 followUp: function () { 4332 if (this.followUpCheck()) { 4333 SBF.ajax({ 4334 function: 'execute-bot-message', 4335 conversation_id: this.conversation.id, 4336 name: 'follow_up', 4337 check: false 4338 }, (response) => { 4339 if (response.settings.sound && this.audio) { 4340 this.audio.play(); 4341 } 4342 this.skip = true; 4343 SBF.storageTime('email'); 4344 SBF.event('SBFollowUp'); 4345 }); 4346 } 4347 }, 4348 4349 followUpCheck: function () { 4350 return !admin && this.conversation && CHAT_SETTINGS.follow && activeUser() && !activeUser().email && SBF.storageTime('email', 24); 4351 }, 4352 4353 // Welcome message 4354 welcome: function () { 4355 let is_valid = !tickets && (CHAT_SETTINGS.welcome_trigger != 'open' || this.chat_open) && (CHAT_SETTINGS.office_hours || !CHAT_SETTINGS.welcome_disable_office_hours) && CHAT_SETTINGS.welcome && !storage('welcome') && activeUser(); 4356 setTimeout(() => { 4357 if (is_valid) { 4358 if (this.conversation && this.conversation.searchMessages('welcome-message')) { 4359 return; 4360 } 4361 SBF.ajax({ 4362 function: 'get-block-setting', 4363 value: 'welcome' 4364 }, (response) => { 4365 this.sendMessage(bot_id, response.message, [], false, { type: 'welcome-message' }, 3); 4366 if (response.open && !mobile) { 4367 this.start(); 4368 } 4369 if (response.sound) { 4370 this.audio.play(); 4371 } 4372 this.skip = true; 4373 SBF.event('SBWelcomeMessage'); 4374 4375 }); 4376 } 4377 }, parseInt(tickets ? 0 : CHAT_SETTINGS.welcome_delay) * 1000); 4378 if (is_valid) { 4379 storage('welcome', true); 4380 } 4381 }, 4382 4383 // Offline timetable message 4384 offlineMessage: function () { 4385 if (!admin && CHAT_SETTINGS.timetable && (!CHAT_SETTINGS.office_hours || (!agents_online && !CHAT_SETTINGS.timetable_disable_agents))) { 4386 let message = CHAT_SETTINGS.timetable_message; 4387 switch (CHAT_SETTINGS.timetable_type) { 4388 case 'header': 4389 if (!this.offline_message_set) { 4390 if (message[0]) { 4391 chat_header.find('.sb-title').html(message[0]); 4392 } 4393 chat_header.find('.sb-text').html(message[1]); 4394 this.offline_message_set = true; 4395 } 4396 break; 4397 case 'info': 4398 if (!this.offline_message_set) {
4399 chat.prepend(`<div class="sb-notify-message sb-rich-cnt"><div class="sb-cnt"><div class="sb-message">${message[0] ? `<b>${message[0]}</b> ` : ''}${message[1]}</div></div></div>`); 4400 main.addClass('sb-notify-active'); 4401 this.offline_message_set = true; 4402 } 4403 break; 4404 default: 4405 setTimeout(() => { 4406 if (this.conversation) { 4407 let offline_message = CHAT_SETTINGS.timetable_hide ? `${message[0] ? `*${message[0]}*\n` : ''}${message[1]}` : '[timetable]'; 4408 let offline_message_sent = this.conversation.searchMessages(offline_message, true); 4409 offline_message_sent = offline_message_sent.length ? offline_message_sent : false; 4410 if (offline_message_sent) { 4411 let last_agent_message = this.conversation.getLastUserMessage(false, true); 4412 offline_message_sent = !last_agent_message || (last_agent_message.get('index') < offline_message_sent[offline_message_sent.length - 1].get('index') && Date.now() - 3600000) < SBF.unix(offline_message_sent[0].get('creation_time')); 4413 } 4414 if (!offline_message_sent) { 4415 this.sendMessage(bot_id, offline_message); 4416 } 4417 } 4418 }, 5000); 4419 } 4420 } 4421 }, 4422 4423 // Delete message 4424 deleteMessage: function (message_id) { 4425 SBF.ajax({ 4426 function: 'delete-message', 4427 message_id: message_id 4428 }, () => { 4429 if (this.conversation) this.conversation.deleteMessage(message_id); 4430 chat.find(`[data-id="${message_id}"]`).remove(); 4431 SBF.event('SBMessageDeleted', message_id); 4432 }); 4433 }, 4434 4435 // Registration form 4436 registration: function (check = false, type = CHAT_SETTINGS.registration_required) { 4437 if (check) { 4438 return CHAT_SETTINGS.registration_required && (!CHAT_SETTINGS.registration_offline || !agents_online) && (typeof SB_DEFAULT_USER == ND || !SB_DEFAULT_USER.email) && (!CHAT_SETTINGS.registration_timetable || !CHAT_SETTINGS.office_hours) && (activeUser() === false || ['visitor', 'lead'].includes(activeUser().type)); 4439 } 4440 chat_scroll_area.append(SBRichMessages.generate({}, CHAT_SETTINGS.registration_link || (CHAT_SETTINGS.registration_required == 'registration-login') ? 'login' : type, 'sb-init-form')); 4441 if (!this.dashboard) { 4442 this.showDashboard(); 4443 } 4444 this.dashboard = true; 4445 this.finalizeInit(); 4446 main.addClass('sb-init-form-active'); 4447 }, 4448 4449 // Shortcut for add user and login function 4450 addUserAndLogin: function (onSuccess = false, lead = false) { 4451 let settings = typeof SB_DEFAULT_USER != ND && SB_DEFAULT_USER ? SB_DEFAULT_USER : {}; 4452 settings.user_type = lead ? 'lead' : 'visitor'; 4453 SBF.ajax({ 4454 function: 'add-user-and-login', 4455 settings: settings, 4456 settings_extra: settings.extra 4457 }, (response) => { 4458 if (SBF.errorValidation(response)) { 4459 if (response[1] == 'duplicate-email' || response[1] == 'duplicate-phone') { 4460 delete SB_DEFAULT_USER.email; 4461 delete SB_DEFAULT_USER.extra.phone; 4462 return this.addUserAndLogin(onSuccess, lead); 4463 } 4464 } else { 4465 SBF.loginCookie(response[1]); 4466 activeUser(new SBUser(response[0])); 4467 SBPusher.start(); 4468 if (!SBPusher.active) { 4469 SBChat.automations.runAll(); 4470 } 4471 if (onSuccess) { 4472 onSuccess(response); 4473 } 4474 } 4475 }); 4476 }, 4477 4478 // Check if the dashboard must be showed 4479 isInitDashboard: function () { 4480 return CHAT_SETTINGS.init_dashboard || (activeUser() && activeUser().conversations.length > 1); 4481 }, 4482 4483 // Upload response 4484 uploadResponse: function (response) { 4485 response = JSON.parse(response); 4486 if (response[0] == 'success') { 4487 if (response[1] == 'extension_error') { 4488 let message = 'The file you are trying to upload has an extension that is not allowed.'; 4489 if (admin) { 4490 SBAdmin.infoPanel(message, 'info'); 4491 } else { 4492 alert(message); 4493 } 4494 } else if ($(upload_target).hasClass('sb-input-image')) { 4495 let image = $(upload_target).find('.image'); 4496 let image_url = image.attr('data-value'); 4497 if (image_url && !image_url.includes('media/user.svg')) { 4498 SBF.ajax({ function: 'delete-file', path: image_url }); 4499 } 4500 image.attr('data-value', '').css('background-image', ''); 4501 setTimeout(() => { 4502 image.attr('data-value', response[1]).css('background-image', `url("${response[1]}?v=${SBF.random()}")`).append('<i class="sb-icon-close"></i>'); 4503 upload_target = false;
4504 }, 500); 4505 } else { 4506 let name = SBF.beautifyAttachmentName(response[1].substr(response[1].lastIndexOf('/') + 1)); 4507 chat_editor.find('.sb-attachments').append(`<div data-name="${name}" data-type="${SBF.getFileIcon(name)}" data-value="${response[1]}"${response.length > 2 ? ' data-size="' + response[2][0] + '|' + response[2][1] + '"' : ''}>${name}<i class="sb-icon-close"></i></div>`); 4508 chat_editor.sbActive(true); 4509 } 4510 } else { 4511 SBF.error(response[1], 'sb-upload-files.change'); 4512 } 4513 this.busy(false); 4514 }, 4515 4516 // Archive a conversation and close it 4517 closeChat: function (update_conversation_status = true) { 4518 let id = this.conversation.id; 4519 SBChat.clear(); 4520 if (update_conversation_status) { 4521 SBF.ajax({ 4522 function: 'update-conversation-status', 4523 conversation_id: id, 4524 status_code: 3 4525 }, () => { 4526 this.closeChat_(id); 4527 }); 4528 } else { 4529 this.closeChat_(id); 4530 } 4531 4532 }, 4533 4534 closeChat_(id) { 4535 main.find(`li[data-conversation-id="${id}"]`).remove(); 4536 force_action = 'new-conversation'; 4537 SBChat.clear(); 4538 storage('open-conversation', ''); 4539 storage('welcome', ''); 4540 storage('flow_on_load', ''); 4541 activeUser().removeConversation(id); 4542 if (!CHAT_SETTINGS.disable_dashboard) { 4543 SBChat.showDashboard(); 4544 } 4545 }, 4546 4547 conversationArchived: function () { 4548 let is_close_chat = (!tickets && CHAT_SETTINGS.close_chat) || (tickets && CHAT_SETTINGS.tickets_close); 4549 if (is_close_chat && !CHAT_SETTINGS.rating) { 4550 return this.closeChat(false); 4551 } 4552 if (CHAT_SETTINGS.rating) { 4553 SBRichMessages.rating(is_close_chat); 4554 } 4555 }, 4556 4557 // Automations 4558 automations: { 4559 history: [], 4560 busy: [], 4561 scroll_position_intervals: {}, 4562 timeout_queue: [], 4563 4564 runAll: function () { 4565 let automations = CHAT_SETTINGS.automations; 4566 for (var i = 0; i < automations.length; i++) { 4567 let automation = automations[i]; 4568 let conditions = automation.conditions; 4569 let count = conditions.length; 4570 let valid = count == 0; 4571 let browsing_time = false; 4572 let scroll_position = false; 4573 let server_conditions = false; 4574 for (var j = 0; j < conditions.length; j++) { 4575 let criteria = conditions[j][1]; 4576 valid = false; 4577 switch (conditions[j][0]) { 4578 case 'browsing_time': 4579 valid = true; 4580 browsing_time = criteria; 4581 break; 4582 case 'scroll_position': 4583 valid = true; 4584 scroll_position = criteria; 4585 break; 4586 case 'referring': 4587 case 'url': 4588 let url = conditions[j][0] == 'referring' ? document.referrer : window.location.href; 4589 let checks = conditions[j][2].replace(/https?:\/\/|www\./g, '').split(','); 4590 url = url.replace(/https?:\/\/|www\./g, ''); 4591 for (var y = 0; y < checks.length; y++) { 4592 if (url.includes(checks[y])) { 4593 valid = criteria == 'contains'; 4594 break; 4595 } 4596 } 4597 break; 4598 case 'include_urls': // Deprecated whole block 4599 case 'exclude_urls': // Deprecated whole block 4600 let url2 = conditions[j][0] == 'referring' ? document.referrer : window.location.href; 4601 let checks2 = conditions[j][2].replace(/https?:\/\/|www\./g, '').split(','); 4602 let include = conditions[j][0] != 'exclude_urls'; 4603 if (!include) valid = true; 4604 url2 = url2.replace(/https?:\/\/|www\./g, ''); 4605 for (var y = 0; y < checks2.length; y++) { 4606 checks2[y] = $.trim(checks2[y].replace('https://', '').replace('http://', '').replace('www.', '')); 4607 if ((criteria == 'contains' && url2.indexOf(checks2[y]) != -1) || (criteria == 'does-not-contain' && url2.indexOf(checks2[y]) == -1) || (criteria == 'is-exactly' && checks2[y] == url2) || (criteria == 'is-not' && checks2[y] != url2)) { 4608 valid = include; 4609 break; 4610 } 4611 } 4612 break; 4613 case 'custom_variable': 4614 let variable = criteria.split('='); 4615 if (variable[0] in window && window[variable[0]] == variable[1]) { 4616 valid = true; 4617 } 4618 break; 4619 case 'returning_visitor': 4620 case 'user_type': 4621 case 'cities': 4622 case 'languages': 4623 case 'countries': 4624 case 'postal_code': 4625 case 'website': 4626 case 'company': 4627 case 'creation_time': 4628 case 'last_activity': 4629 valid = activeUser(); 4630 server_conditions = true; 4631 break; 4632 case 'phone': 4633 valid = activeUser() && activeUser().getExtra('phone'); 4634 break; 4635 case 'email': 4636 valid = activeUser() && activeUser().email; 4637 break; 4638 default: 4639 valid = activeUser() && activeUser().getExtra(conditions[j][0]); 4640 } 4641 if (!valid) { 4642 break; 4643 } 4644 } 4645 if (['messages', 'emails', 'sms'].includes(automation.type) && !activeUser()) { 4646 valid = false;
4647 } 4648 if (valid) { 4649 if (server_conditions) { 4650 if (!(automation.id in this.busy)) { 4651 SBF.ajax({ 4652 function: 'automations-validate', 4653 automation: automation 4654 }, (response) => { 4655 if (response !== false) { 4656 this.runAll_final(automation, scroll_position, browsing_time); 4657 } 4658 delete this.busy[automation.id]; 4659 }); 4660 this.busy[automation.id] = true; 4661 } 4662 } else if (automation.type != 'messages' || !SBChat.registration(true)) { 4663 this.runAll_final(automation, scroll_position, browsing_time); 4664 } 4665 } 4666 } 4667 }, 4668 4669 runAll_final: function (automation, scroll_position, browsing_time) { 4670 if (scroll_position) { 4671 this.scroll_position_intervals[automation.id] = setInterval(() => { 4672 if ($(window).scrollTop() > parseInt(scroll_position)) { 4673 if (browsing_time) { 4674 setTimeout(() => { this.run(automation) }, parseInt(browsing_time) * 1000); 4675 } else { 4676 this.run(automation); 4677 } 4678 clearInterval(this.scroll_position_intervals[automation.id]); 4679 } 4680 }, 1000); 4681 } else if (browsing_time) { 4682 if (!this.timeout_queue.includes(automation.id)) { 4683 setTimeout(() => { this.run(automation) }, parseInt(browsing_time) * 1000); 4684 this.timeout_queue.push(automation.id); 4685 } 4686 } else this.run(automation); 4687 }, 4688 4689 run: function (automation) { 4690 if (this.history.includes(automation.id)) return; 4691 switch (automation.type) { 4692 case 'messages': 4693 case 'emails': 4694 case 'sms': 4695 if ((!SBPusher.active || SBPusher.started) && !(automation.id in this.busy)) { 4696 if (automation.type == 'messages' && SBChat.chat_open) { 4697 let last_message = SBChat.conversation ? SBChat.conversation.getLastUserMessage(false, 'no-bot') : false; 4698 if (last_message && ((Date.now() - 600000) < SBF.unix(last_message.get('creation_time')))) { 4699 return; 4700 } 4701 } 4702 SBF.ajax({ 4703 function: 'automations-run', 4704 automation: automation 4705 }, (response) => { 4706 if (response !== false) { 4707 this.history.push(automation.id); 4708 if (automation.type == 'messages' && !SBPusher.active) SBChat.updateConversations(); 4709 } 4710 delete this.busy[automation.id]; 4711 }); 4712 this.busy[automation.id] = true; 4713 } 4714 break; 4715 case 'popups': 4716 if (!storage('popup' + automation.id)) { 4717 if (!SBChat.chat_open) { 4718 setTimeout(() => { 4719 SBChat.popup(false, { id: automation.id, image: automation.profile_image, title: automation.title, message: automation.message }); 4720 }, 1000); 4721 this.history.push(automation.id); 4722 } else if (automation.fallback) { 4723 let last_message = SBChat.conversation ? SBChat.conversation.getLastUserMessage(false, 'no-bot') : false;
4724 if (!last_message || ((Date.now() - 600000) > SBF.unix(last_message.get('creation_time')))) { 4725 SBChat.sendMessage(bot_id, (SBF.null(automation.title) ? '' : `*${automation.title}*\n`) + automation.message, [], false, false, 0); 4726 storage('popup' + automation.id, true); 4727 this.history.push(automation.id); 4728 } 4729 } 4730 } 4731 break; 4732 case 'design': 4733 if (automation.background) { 4734 chat_header.css('background-image', `url("${automation.background}")`); 4735 } 4736 if (automation.brand) { 4737 chat_header.find('.sb-brand img').attr('src', automation.brand); 4738 } 4739 if (automation.title) { 4740 chat_header.find('.sb-title').html(automation.title); 4741 } 4742 if (automation.message) { 4743 chat_header.find('.sb-text').html(automation.message); 4744 } 4745 if (automation.icon) { 4746 main.find('.sb-chat-btn .sb-icon').attr('src', automation.icon); 4747 } 4748 if (automation.color_1 || automation.color_2 || automation.color_3) { 4749 SBF.ajax({ function: 'chat-css', color_1: automation.color_1, color_2: automation.color_2, color_3: automation.color_3 }, (response) => { 4750 global.append(`<style>${response}</style>`); 4751 }); 4752 } 4753 this.history.push(automation.id); 4754 break; 4755 case 'more': 4756 let parameters = {}; 4757 if (automation.department) { 4758 SBChat.default_department = automation.department; 4759 parameters = { function: 'update-conversation-department', department: automation.department }; 4760 } 4761 if (automation.agent) { 4762 SBChat.default_agent = automation.agent; 4763 parameters = { function: 'update-conversation-agent', agent_id: automation.agent }; 4764 } 4765 if (automation.tags) { 4766 automation.tags = automation.tags.split(','); 4767 SBChat.default_tags = automation.tags; 4768 parameters = { function: 'update-tags', tags: automation.tags, add: true }; 4769 } 4770 if (SBChat.conversation.id && (automation.tags || automation.agent || automation.department)) { 4771 parameters.conversation_id = SBChat.conversation.id 4772 SBF.ajax(parameters); 4773 } 4774 if (automation.articles || automation.articles_category) { 4775 let articles_dashboard = main.find('.sb-dashboard-articles > .sb-articles'); 4776 SBChat.articles_allowed_ids = automation.articles; 4777 SBChat.articles_category = automation.articles_category; 4778 if (articles_dashboard) { 4779 SBChat.getArticles(automation.articles, (response) => { 4780 let code = ''; 4781 for (var i = 0; i < 2; i++) { 4782 code += `<div data-id="${response[i].id}"><div>${response[i].title}</div><span>${response[i].content}</span></div>`; 4783 } 4784 articles_dashboard.html(code); 4785 }, automation.articles_category, 2); 4786 } 4787 } 4788 this.history.push(automation.id); 4789 break; 4790 } 4791 } 4792 }, 4793 4794 // More 4795 flashNotification: function () { 4796 clearInterval(interval); 4797 interval = setInterval(function () { 4798 let count = admin ? SBAdmin.conversations.getNotificationsCounterCount() : SBChat.notifications.length; 4799 if (count) { 4800 document.title = document.title == document_title ? '(' + count + ') ' + sb_('New message' + (count > 1 ? 's' : '')) : document_title; 4801 } 4802 }, 2000); 4803 }, 4804 4805 calculateLabelDates: function () { 4806 if (admin || this.chat_open) { 4807 label_date_items = chat.find('.sb-label-date'); 4808 } 4809 }, 4810 4811 calculateLabelDateFirst: function () { 4812 if (!this.conversation.messages.length) { 4813 chat.append(`<div class="sb-label-date"><span>${sb_('Today')}</span></div>`); 4814 } 4815 }, 4816 4817 playSound: function (repeat = false) { 4818 this.audio.play(); 4819 let index = repeat ? repeat : (admin ? SB_ADMIN_SETTINGS.sound.repeat : CHAT_SETTINGS.sound.repeat); 4820 if (index && !this.tab_active) { 4821 clearInterval(this.audio_interval); 4822 this.audio_interval = setInterval(() => { 4823 this.audio.play(); 4824 index--; 4825 if (!index) { 4826 clearInterval(this.audio_interval); 4827 } 4828 }, this.audio.duration * 1000 + 1500); 4829 } 4830 }, 4831 4832 isConversationAllowed: function (source, status_code, payload) { 4833 return (!CHAT_SETTINGS.tickets_hide || (tickets && source == 'tk') || (!tickets && source != 'tk')) && (![3, 4, '3', '4'].includes(status_code) || (payload && payload.includes('-message') || ((!tickets && !CHAT_SETTINGS.close_chat) || (tickets && !CHAT_SETTINGS.tickets_close)))); 4834 } 4835 } 4836 window.SBChat = SBChat; 4837 4838 /* 4839 * ---------------------------------------------------------- 4840 * RICH MESSAGES 4841 * ---------------------------------------------------------- 4842 */ 4843 4844 var SBRichMessages = { 4845 rich_messsages: { 4846 email: '', 4847 button: '', 4848 video: '', 4849 image: '', 4850 woocommerce_button: '', 4851 rating: '', 4852 chips: '<div class="sb-buttons">[options]</div>', 4853 buttons: '<div class="sb-buttons">[options]</div>', 4854 select: '<div class="sb-select"><p></p><ul>[options]</ul></div>', 4855 list: '<div class="sb-text-list">[values]</div>', 4856 'list-image': '<div class="sb-image-list">[values]</div>', 4857 table: '<table><tbody>[header][values]</tbody></table>', 4858 inputs: '<div class="sb-form">[values]</div>', 4859 card: '<div class="sb-card">[settings]</div>', 4860 share: '<div class="sb-social-buttons">[settings]</div>', 4861 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>', 4862 '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>' 4863 }, 4864 cache: {}, 4865 duplicated_email: false, 4866 4867 // Generate a rich message 4868 generate: function (settings, name, css = '') { 4869 let content; 4870 let next = true; 4871 let id = settings.id ? settings.id : SBF.random(); 4872 let render = new SBMessage({}); 4873 4874 // Check if the rich message exist 4875 if (name in this.rich_messsages) { 4876 content = this.rich_messsages[name]; 4877 } else if (name in this.cache) { 4878 content = this.cache[name]; 4879 } else if (this.isShortcode(name)) { 4880 if (!settings.id) {
4881 id = name; 4882 } 4883 content = '<div class="sb-rich-loading sb-loading"></div>'; 4884 SBF.ajax({ 4885 function: 'get-rich-message', 4886 name: name, 4887 settings: settings 4888 }, (response) => { 4889 response = this.initInputs(response); 4890 if (name == 'timetable') { 4891 response = this.timetable(response); 4892 } 4893 $(admin && SBAdmin.active_admin_area == 'chatbot' ? '.sb-playground' : main).find(`.sb-rich-message[id="${id}"]`).html(`<div class="sb-content">${response}</div>`); 4894 this.cache[name] = response; 4895 SBChat.scrollBottom(SBChat.dashboard); 4896 SBF.event('SBRichMessageShown', { name: name, settings: settings, response: response }); 4897 }); 4898 next = false; 4899 } else { 4900 return false; 4901 } 4902 4903 // Generate the rich message 4904 let disabled = settings.disabled; 4905 if (next) { 4906 let options; 4907 let code = ''; 4908 switch (name) { 4909 case 'email': 4910 let inputs = []; 4911 let email = activeUser().email; 4912 let default_name = activeUser().get('last_name').charAt(0) == '#'; 4913 if (settings['name'] == 'true') { 4914 inputs.push(['first_name', settings['last-name'] == 'true' ? 'First name' : 'Name', default_name ? '' : (settings['last-name'] == 'true' ? activeUser().get('first_name') : activeUser().name), 'text', true]); 4915 } 4916 if (settings['last-name'] == 'true') { 4917 inputs.push(['last_name', 'Last name', default_name ? '' : activeUser().get('last_name'), 'text', true]); 4918 } 4919 for (var i = 0; i < inputs.length; i++) { 4920 content += `<div id="${inputs[i][0]}" data-type="text" class="sb-input sb-input-text"><span class="${inputs[i][2] ? 'sb-active sb-filled' : ''}">${sb_(inputs[i][1])}</span><input value="${inputs[i][2]}" autocomplete="false" type="${inputs[i][3]}" ${inputs[i][4] ? 'required' : ''}></div>`; 4921 } 4922 if (settings['phone'] == 'true') { 4923 let phone = activeUser().getExtra('phone'); 4924 if (!this.cache.phone) { 4925 this.cache.phone = true; 4926 SBF.ajax({ 4927 function: 'get-select-phone' 4928 }, (response) => { 4929 this.cache.phone = response; 4930 chat.find('#phone .sb-select-phone').html(response); 4931 }); 4932 } 4933 content += `<div id="phone" data-type="select-input" class="sb-input sb-input-select-input"><span class="${phone ? 'sb-active sb-filled' : ''}">${sb_('Phone')}</span><div class="sb-select-phone">${this.cache.phone ? this.cache.phone : ''}</div><input autocomplete="false" type="text" data-phone="true"${settings['phone-required'] != 'false' ? ' required' : ''}></div>`; 4934 } 4935 content += `<div id="email" data-type="email" class="sb-input sb-input-btn"><span class="${email ? 'sb-active sb-filled' : ''}">${sb_(SBF.null(settings.placeholder) ? 'Email' : settings.placeholder)}</span><input value="${email}" autocomplete="off" type="email" required><div class="sb-submit sb-icon-arrow-right"></div></div><div id="otp" class="sb-input"><span>${sb_('One-time code')}</span><input autocomplete="false" type="text"></div>`; 4936 break; 4937 case 'image': 4938 content = `<div class="sb-image"><img loading="lazy" src="${settings.url}"></div>`; 4939 break; 4940 case 'video': 4941 content = `<iframe loading="lazy"${settings.height ? ` height="${settings.height}"` : ''} src="https://${settings.type == 'youtube' ? 'www.youtube.com/embed/' : 'player.vimeo.com/video/'}${settings.id}" allowfullscreen></iframe>`; 4942 break; 4943 case 'select': 4944 options = settings && settings.options ? settings.options.replace(/\\,/g, '{R}').split(',') : []; 4945 for (var i = 0; i < options.length; i++) { 4946 let item = options[i].replace(/{R}/g, ','); 4947 code += `<li data-value="${SBF.stringToSlug(item)}">${sb_(item)}</li>`; 4948 } 4949 content = content.replace('[options]', code); 4950 break; 4951 case 'chips': 4952 case 'buttons': 4953 options = settings && settings.options ? settings.options.replace(/\\,/g, '{R}').split(',') : []; 4954 for (var i = 0; i < options.length; i++) { 4955 code += `<div class="sb-btn sb-submit">${sb_(options[i].replace(/{R}/g, ',').split('|')[0])}</div>`; 4956 } 4957 content = content.replace('[options]', code); 4958 break; 4959 case 'button': 4960 if (settings && settings.link) { 4961 let action_link = settings.link.includes('calendly.com') ? 'calendly' : (settings.link.includes('tidycal.com') ? 'tidycal' : false); 4962 content = `<a ${action_link ? `data-action="${action_link}" data-extra="${settings.link}|${settings.success ? settings.success.replaceAll('"', '\'') : ''}" ` : ''}href="${action_link ? '#' : settings.link.replace(/<i>/g, '_').replace(/<\/i>/g, '_')}"${settings.target && !action_link ? ' target="_blank"' : ''} class="sb-rich-btn sb-btn${settings.style == 'link' ? '-text' : ''}">${sb_(settings.name)}</a>`; 4963 } 4964 break; 4965 case 'list': 4966 if (settings.values) { 4967 options = settings.values.replace(/\\,/g, '{R}').replace(/\\:/g, '{R2}').replace(/:\/\//g, '{R3}').split(','); 4968 let list = name == 'list'; 4969 let list_double = list && options.length && options[0].indexOf(':') > 0; 4970 if (list && !list_double) { 4971 content = content.replace('sb-text-list', 'sb-text-list sb-text-list-single'); 4972 } 4973 if (settings.numeric) { 4974 content = content.replace('sb-text-list', 'sb-text-list sb-text-list-numeric'); 4975 } 4976 for (var i = 0; i < options.length; i++) { 4977 let item = options[i].replace(/{R}/g, ','); 4978 let is_inner = item.substr(0, 1) === '-'; 4979 code += list_double && item.includes(':') ? `<div><div>${sb_(item.split(':')[0].replace(/{R2}/g, ':').replace(/{R3}/g, '://'))}</div><div>${sb_(item.split(':')[1].replace(/{R2}/g, ':').replace(/{R3}/g, '://'))}</div></div>` : `<div${is_inner ? ' data-inner="true"' : ''}>${$.trim(sb_((is_inner ? item.substr(1) : item).replace(/{R2}/g, ':').replace(/{R3}/g, '://')))}</div>`; 4980 } 4981 content = content.replace('[values]', code); 4982 } 4983 break; 4984 case 'list-image': 4985 if (settings.values) { 4986 options = settings.values.split(','); 4987 for (var i = 0; i < options.length; i++) { 4988 let item = options[i].replace('://', '///').split(':'); 4989 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>`; 4990 } 4991 content = content.replace('[values]', code); 4992 } 4993 break; 4994 case 'table': 4995 if (settings.values) { 4996 options = settings.header.split(','); 4997 code += '<tr>'; 4998 for (var i = 0; i < options.length; i++) { 4999 code += `<th>${options[i]}</th>`; 5000 } 5001 code += '</tr>'; 5002 content = content.replace('[header]', code); 5003 code = ''; 5004 options = settings.values.split(','); 5005 for (var i = 0; i < options.length; i++) {
5006 let tds = options[i].split(':'); 5007 code += '<tr>'; 5008 for (var j = 0; j < tds.length; j++) { 5009 code += `<td>${tds[j]}</td>`; 5010 } 5011 code += '</tr>'; 5012 } 5013 content = content.replace('[values]', code); 5014 } 5015 break; 5016 case 'inputs': 5017 if (settings.values) { 5018 options = settings.values.split(','); 5019 for (var i = 0; i < options.length; i++) { 5020 if (disabled && !options[i]) continue; 5021 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>`; 5022 } 5023 code += '<div class="sb-btn sb-submit">' + sb_(settings.button ? settings.button : 'Send now') + '</div>'; 5024 content = content.replace('[values]', code); 5025 } 5026 break; 5027 case 'card': 5028 code = `${settings.image ? `<div class="sb-card-img" style="background-image:url('${settings.image}')"></div>` : ''}<div class="sb-card-header">${settings.header}</div>${settings.extra ? `<div class="sb-card-extra">${settings.extra}</div>` : ''}${settings.description ? `<div class="sb-card-description">${settings.description}</div>` : ''}${settings.link ? `<a class="sb-card-btn" href="${settings.link}"${settings.target ? ' target="_blank"' : ''}>${sb_(settings['link-text'])}</a>` : ''}`; 5029 content = content.replace('[settings]', code); 5030 break; 5031 case 'share': 5032 let channels = settings.channels ? settings.channels.replace(/ /g, '').split(',') : ['fb', 'tw', 'li', 'wa', 'pi']; 5033 let link = ''; 5034 for (var i = 0; i < channels.length; i++) { 5035 switch (channels[i]) { 5036 case 'fb': 5037 link = 'www.facebook.com/sharer.php?u='; 5038 break; 5039 case 'tw': 5040 link = 'twitter.com/intent/tweet?url='; 5041 break; 5042 case 'li': 5043 link = 'www.linkedin.com/sharing/share-offsite/?url='; 5044 break; 5045 case 'wa': 5046 link = 'web.whatsapp.com/send?text='; 5047 break; 5048 case 'pi': 5049 link = 'www.pinterest.com/pin/create/button/?url='; 5050 break; 5051 } 5052 code += `<div class="sb-${channels[i]} sb-icon-social-${channels[i]}" data-link="https://${link}${encodeURIComponent(settings[channels[i]])}"></div>`; 5053 } 5054 content = content.replace('[settings]', code); 5055 break; 5056 case 'slider': 5057 let count = 0; 5058 for (var i = 1; i < 16; i++) { 5059 if (('header-' + i) in settings) { 5060 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]}"${settings.target ? ' target="_blank"' : ''}>${sb_(settings['link-text-' + i])}</a>` : ''}</div>`; 5061 count++; 5062 } else { 5063 break; 5064 } 5065 } 5066 content = content.replace('[items]', code).replace(/\[class\]/g, count == 1 ? ' sb-hide' : ''); 5067 break; 5068 case 'slider-images': 5069 if (settings.images) { 5070 let images = settings.images.split(','); 5071 for (var i = 0; i < images.length; i++) { 5072 code += `<div class="sb-card-img" data-value="${images[i]}" style="background-image:url('${images[i]}')"></div>`; 5073 } 5074 content = content.replace(/\[class\]/g, images.length == 1 ? ' sb-hide' : ''); 5075 } 5076 content = content.replace('[items]', code); 5077 break; 5078 case 'woocommerce_button': 5079 settings.settings = `checkout:${settings.checkout},coupon:${settings.coupon}`; 5080 content = `<a href="#" data-ids="${settings.ids}" class="sb-rich-btn sb-btn">${settings.name}</a>`; 5081 break; 5082 case 'rating': 5083 content = `<div class="sb-rating-message sb-rating-${settings.value == 1 ? 'positive' : 'negative'}"><div><i class="sb-icon-${settings.value == 1 ? 'like' : 'dislike'}"></i> ${sb_(settings.value == 1 ? 'Helpful' : 'Not helpful')}</div>${settings.message ? '<div>' + settings.message + '</div>' : ''}</div>`; 5084 settings.message = false;
5085 break; 5086 } 5087 } 5088 return `<div id="${id}" data-type="${name}"${disabled ? 'disabled="true"' : ''}${settings.settings ? ` data-settings="${settings.settings}"` : ''}class="sb-rich-message sb-rich-${name} ${css}">` + (settings.title ? `<div class="sb-top">${render.render(sb_(settings.title))}</div>` : '') + (settings.message ? `<div class="sb-text">${render.render(sb_(settings.message))}</div>` : '') + `<div class="sb-content">${content}</div>${name == 'email' ? `<div data-success="${settings.success ? settings.success.replace(/"/g, '') : ''}" class="sb-info"></div>` : ''}</div>`; 5089 }, 5090 5091 // Function of built-in rich messages 5092 submit: function (area, type, element) { 5093 if (!admin && !loading(element) && !this.is_busy) { 5094 let error = ''; 5095 let shortcode = ''; 5096 let parameters = {}; 5097 let success = $(area).find('[data-success]').length ? $(area).find('[data-success]').attr('data-success') : ''; 5098 let rich_message_id = $(area).closest('.sb-rich-message').attr('id'); 5099 let message_id = $(area).closest('[data-id]').attr('data-id'); 5100 let message = ''; 5101 let payload = { 'rich-messages': {} }; 5102 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().email, password: '', user_type: '' }; 5103 let settings = {}; 5104 let input = $(element); 5105 let active_conversation = SBChat.conversation !== false; 5106 let settings_extra = {}; 5107 let payload_settings = {}; 5108 let otp = area.find('#otp'); 5109 if (SBF.null(message_id)) { 5110 message_id = -1; 5111 } else { 5112 let item = SBChat.conversation.getMessage(message_id); 5113 message = item.message; 5114 payload = item.payload(); 5115 if (!payload['rich-messages']) { 5116 payload['rich-messages'] = {}; 5117 } 5118 } 5119 if (!$(element).hasClass('sb-btn') && !$(element).hasClass('sb-select') && !$(element).hasClass('sb-submit')) { 5120 input = $(element).closest('.sb-btn,.sb-select'); 5121 } 5122 $(area).find('.sb-info').html('').sbActive(false); 5123 switch (type) { 5124 case 'email': 5125 settings = SBForm.getAll(area); 5126 $.each(settings, function (key, value) { 5127 settings[key] = value[0]; 5128 }); 5129 if (settings.first_name) { 5130 user_settings.user_type = 'user'; 5131 if (!settings.last_name) { 5132 user_settings.last_name = ''; 5133 } 5134 } 5135 if (settings.phone) { 5136 settings_extra = { phone: [settings.phone, 'Phone'] }; 5137 } 5138 $.extend(user_settings, settings); 5139 error = 'Please fill in all required fields and make sure the email is valid.'; 5140 if (success) { 5141 success = sb_(success).replace('{user_email}', user_settings.email).replace('{user_name_}', user_settings.first_name + (settings.last_name ? (' ' + user_settings.last_name) : '')); 5142 } 5143 if (otp.sbActive()) { 5144 let otp_string = otp.attr('data-otp'); 5145 user_settings.otp = otp_string ? [otp_string, area.find('#otp input').val()] : false; 5146 } 5147 payload['rich-messages'][rich_message_id] = { type: type, result: settings }; 5148 payload['event'] = 'update-user'; 5149 parameters = { function: 'update-user-and-message', settings: user_settings, settings_extra: settings_extra, payload: payload, skip_otp: true }; 5150 break; 5151 case 'registration': 5152 settings = SBForm.getAll(area.find('.sb-form-main')); 5153 settings_extra = SBForm.getAll(area.find('.sb-form-extra')); 5154 $.each(settings, function (key, value) { 5155 settings[key] = value[0]; 5156 }); 5157 $.extend(user_settings, settings); 5158 payload_settings = $.extend({}, user_settings); 5159 if (success) { 5160 success = sb_(success); 5161 } 5162 if (CHAT_SETTINGS.registration_details) { 5163 success += '[list values="'; 5164 for (var key in user_settings) { 5165 let value = user_settings[key].replace(/:|,/g, ''); 5166 if (value) { 5167 if (key == 'profile_image') { 5168 value = value.substr(value.lastIndexOf('/') + 1); 5169 } 5170 if (['password', 'password-check', 'envato-purchase-code', 'otp'].includes(key)) { 5171 value = '********'; 5172 payload_settings[key] = '********'; 5173 } else { 5174 success += user_settings[key] ? `${sb_(SBF.slugToString(key.replace('first_name', 'name')))}:${value},` : ''; 5175 } 5176 } 5177 } 5178 for (var key in settings_extra) { 5179 if (settings_extra[key][0]) { 5180 success += `${sb_(settings_extra[key][1].replace(/:|,/g, ''))}:${settings_extra[key][0].replace(/:|,/g, '')},`; 5181 } 5182 } 5183 success = success.slice(0, -1) + '"]'; 5184 } 5185 if (otp.sbActive()) { 5186 let otp_string = otp.attr('data-otp'); 5187 user_settings.otp = otp_string ? [otp_string, area.find('#otp input').val()] : false;
5188 } 5189 user_settings.user_type = 'user'; 5190 payload['rich-messages'][rich_message_id] = { type: type, result: { user: payload_settings, extra: settings_extra } }; 5191 payload['event'] = 'update-user'; 5192 parameters = CHAT_SETTINGS.registration_otp && user_settings.email && !user_settings.otp ? { function: 'otp', email: user_settings.email } : { function: activeUser() ? 'update-user-and-message' : 'add-user-and-login', settings: user_settings, settings_extra: settings_extra, payload: payload }; 5193 error = SBForm.getRegistrationErrorMessage(area); 5194 break; 5195 case 'chips': 5196 case 'select': 5197 case 'buttons': 5198 settings = SBF.escape($(element).html()); 5199 if (success) { 5200 success = sb_(success) + ` *${settings}*`; 5201 } 5202 payload['rich-messages'][rich_message_id] = { type: type, result: settings }; 5203 parameters = { function: 'update-message', payload: payload }; 5204 if (type == 'chips') { 5205 SBChat.sendMessage(activeUser().id, settings, [], false, { id: rich_message_id, event: 'chips-click', result: settings }, rich_message_id == 'sb-human-takeover' && input.index() == 0 ? 2 : false); 5206 if (rich_message_id == 'sb-human-takeover' && $(element).index() == 0) { 5207 SBApps.dialogflow.humanTakeover(); 5208 } 5209 $(element).closest('.sb-content').remove(); 5210 } 5211 break; 5212 case 'inputs': 5213 settings = SBForm.getAll(area); 5214 error = 'All fields are required.'; 5215 if (success) { 5216 success = sb_(success) + ' [list values="'; 5217 for (var key in settings) { 5218 success += `${sb_(settings[key][1].replace(/:|,/g, ''))}:${settings[key][0].replace(/:|,/g, '')},`; 5219 } 5220 success = success.slice(0, -1) + '"]'; 5221 } 5222 payload['rich-messages'][rich_message_id] = { type: type, result: settings }; 5223 parameters = { function: 'update-message', payload: payload }; 5224 break; 5225 } 5226 shortcode = message.substr(message.indexOf('[' + type)) 5227 shortcode = shortcode.substr(0, shortcode.indexOf(']') + 1); 5228 if (error && SBForm.errors(area)) { 5229 SBForm.showErrorMessage(area, error); 5230 input.sbLoading(false); 5231 if (SBChat.dashboard || (active_conversation && SBChat.conversation.getLastMessage().id == message_id)) { 5232 SBChat.scrollBottom(); 5233 } 5234 return false; 5235 } 5236 if (!success && type != 'registration') { 5237 let shortcode_settings = this.shortcode(shortcode); 5238 let id = shortcode_settings[1].id ? `id="${shortcode_settings[1].id}"` : ''; 5239 let title = shortcode_settings[1].title ? `title="${shortcode_settings[1].title}"` : ''; 5240 let message = shortcode_settings[1].message ? `message="${shortcode_settings[1].message}"` : ''; 5241 let value = ''; 5242 if (['inputs', 'email'].includes(type)) { 5243 for (var key in settings) { 5244 if (key != 'otp') { 5245 value += settings[key] + ','; 5246 } 5247 } 5248 value = `values="${value.slice(0, -1)}"` 5249 } else { 5250 value = `options="${settings}"`; 5251 }
5252 success = `[${type == 'email' ? 'inputs' : type} ${id} ${title} ${message} ${value} disabled="true"]`; 5253 } 5254 if (message_id != -1) { 5255 success = success.replace(/<br>/g, '\n'); 5256 $.extend(parameters, { 5257 message_id: message_id, 5258 message: message ? (type == 'chips' ? message.replace(']', ' disabled="true"]') : message.replace(shortcode, success)) : success, 5259 payload: payload 5260 }); 5261 } 5262 SBF.ajax(parameters, (response) => { 5263 if (response && !SBF.errorValidation(response)) { 5264 switch (type) { 5265 case 'email': 5266 for (var key in user_settings) { 5267 activeUser().set(key, user_settings[key]); 5268 } 5269 for (var key in settings_extra) { 5270 activeUser().setExtra(key, settings_extra[key][0]); 5271 } 5272 SBF.loginCookie(response[1]); 5273 if (response[0].user_change) { 5274 location.reload(); 5275 return; 5276 } 5277 if (rich_message_id == 'sb-follow-up') { 5278 SBF.ajax({ 5279 function: 'subscribe-email', 5280 email: activeUser().email 5281 }); 5282 } 5283 SBChat.automations.runAll(); 5284 SBF.event('SBNewEmailAddress', { id: rich_message_id, name: activeUser().name, email: activeUser().email }); 5285 break; 5286 case 'registration': 5287 let otp = area.find('#otp'); 5288 if (CHAT_SETTINGS.registration_otp && !otp.sbActive()) { 5289 otp.attr('data-otp', response).sbActive(true); 5290 otp.find('input').attr('required', true).addClass('sb-error'); 5291 SBForm.showErrorMessage(area, sb_('Please check your email for the one-time code.')); 5292 SBChat.scrollBottom(); 5293 input.sbLoading(false); 5294 return; 5295 } 5296 SBF.loginCookie(response[1]); 5297 user_settings.id = response[0].id; 5298 if (!activeUser()) { 5299 activeUser(new SBUser(response[0])); 5300 for (var key in settings_extra) { 5301 activeUser().setExtra(key, settings_extra[key][0]); 5302 } 5303 if (this.duplicated_email && !CHAT_SETTINGS.init_dashboard) { 5304 force_action = 'open-conversation'; 5305 } 5306 SBPusher.start(); 5307 SBChat.initChat(); 5308 if (!this.duplicated_email && (!CHAT_SETTINGS.init_dashboard || !main.find('.sb-departments-list').length) && success) { 5309 SBChat.sendMessage(bot_id, success, [], false, false, 3); 5310 } 5311 } else { 5312 for (var key in user_settings) { 5313 activeUser().set(key, user_settings[key]); 5314 } 5315 for (var key in settings_extra) { 5316 activeUser().setExtra(key, settings_extra[key][0]); 5317 } 5318 SBChat.automations.runAll(); 5319 SBChat.welcome(); 5320 } 5321 if (SBChat.dashboard) { 5322 main.removeClass('sb-init-form-active'); 5323 $(area).remove(); 5324 if (!SBChat.isInitDashboard() && (!CHAT_SETTINGS.init_dashboard || !this.duplicated_email)) { 5325 SBChat.hideDashboard(); 5326 } 5327 } 5328 if (CHAT_SETTINGS.wp_registration && user_settings.email && user_settings.password) { 5329 SBApps.wordpress.ajax('wp-registration', { user_id: response[0].id, first_name: response[0].first_name, last_name: response[0].last_name, password: user_settings.password, email: user_settings.email }); 5330 } else if (CHAT_SETTINGS.wp_users_system == 'wp') { 5331 SBApps.wordpress.ajax('wp-login', { user: user_settings.email, password: user_settings.password }); 5332 } 5333 delete this.cache.registration; 5334 setTimeout(() => { 5335 SBF.event('SBRegistrationForm', { id: rich_message_id, conversation_id: SBChat.conversation ? SBChat.conversation.id : false, user: user_settings, extra: payload['rich-messages'][rich_message_id]['result']['extra'] }); 5336 }, 5000); 5337 break; 5338 case 'buttons': 5339 SBChat.scrollBottom(); 5340 break; 5341 } 5342 if (message_id == -1) { 5343 $(element).closest('.sb-rich-message').html(success); 5344 } else { 5345 input.sbLoading(false); 5346 if ((!payload.type || payload.type != 'close-message') && !dialogflow_human_takeover) { 5347 SBChat.setConversationStatus(2); 5348 } 5349 } 5350 if (!['login', 'chips'].includes(type) && (CHAT_SETTINGS.dialogflow_send_user_details || !['email', 'registration'].includes(type))) { 5351 SBApps.dialogflow.message(`${rich_message_id}
5351${dialogflow_response ? ('|' + dialogflow_response) : ''}`); 5352 } 5353 if (CHAT_SETTINGS.slack_active && (!dialogflow_human_takeover || SBApps.dialogflow.humanTakeoverActive())) { 5354 SBApps.slack.message(activeUser().id, activeUser().name, activeUser().image, success); 5355 } 5356 if (SBPusher.active) { 5357 SBChat.update(); 5358 } 5359 if (type != 'registration' && type != 'email') { 5360 SBF.event('SBRichMessageSubmit', { result: response, data: payload['rich-messages'][rich_message_id], id: rich_message_id }); 5361 } 5362 } else { 5363 if ((type == 'registration' || type == 'email') && SBF.errorValidation(response, 'duplicate-email')) { 5364 SBF.ajax({ 5365 function: 'otp', 5366 email: user_settings.email 5367 }, (response) => { 5368 let otp = area.find('#otp'); 5369 otp.attr('data-otp', response).sbActive(true); 5370 otp.find('input').attr('required', true).addClass('sb-error'); 5371 SBForm.showErrorMessage(area, sb_('This email is already in use. Please check your email for the one-time code.')); 5372 if (type == 'registration') { 5373 area.find('.sb-submit').html(sb_('Sign in')); 5374 SBChat.scrollBottom(); 5375 } 5376 }); 5377 this.duplicated_email = true; 5378 } else { 5379 this.duplicated_email = false; 5380 SBForm.showErrorMessage(area, SBForm.getRegistrationErrorMessage(response, 'response')); 5381 } 5382 if (SBChat.dashboard) { 5383 SBChat.scrollBottom(); 5384 } 5385 input.sbLoading(false); 5386 } 5387 }); 5388 } 5389 }, 5390 5391 // Return the shortcode name and the shortcode settings 5392 shortcode: function (shortcode) { 5393 let result = {}; 5394 let shortcode_name = shortcode.includes(' ') ? shortcode.substr(1, shortcode.indexOf(' ') - 1) : shortcode.slice(1, -1); 5395 if (/\?|"|'|`|\*/gi.test(shortcode_name)) { 5396 return [false, false]; 5397 } 5398 shortcode = shortcode.slice(1, -1).substr(shortcode_name.length + 1); 5399 let settings = shortcode.split('" '); 5400 for (var i = 0; i < settings.length; i++) { 5401 if (settings[i].includes('=')) { 5402 let item = [settings[i].substr(0, settings[i].indexOf('=')), settings[i].substr(settings[i].indexOf('=') + 2)]; 5403 result[$.trim(item[0])] = item[1].replace(/"/g, ''); 5404 } 5405 } 5406 return [shortcode_name, result]; 5407 }, 5408 5409 // Init the rich message inputs 5410 initInputs: function (code) { 5411 code = $($.parseHTML('<div>' + code + '</div>')); 5412 code.find('.sb-input input').each(function () { 5413 if ($(this).val()) { 5414 $(this).siblings().addClass('sb-active sb-filled'); 5415 } 5416 }); 5417 return code.html(); 5418 }, 5419 5420 // Timetable shortcode 5421 timetable: function (code) { 5422 let table = $($.parseHTML(`<div>${code}</div>`)); 5423 let utc_offset = table.find('[data-offset]').attr('data-offset'); 5424 utc_offset = SBF.null(utc_offset) ? 0 : parseFloat(utc_offset); 5425 table.find('[data-time]').each(function () { 5426 let times = $(this).attr('data-time').split('|'); 5427 code = '' 5428 for (var i = 0; i < times.length; i++) { 5429 if (!times[i] || times[i] == 'closed') { // Deparecated. Remove || times[i] == 'closed' 5430 code += sb_('Closed'); 5431 break; 5432 } else if (times[i]) { 5433 let hm = times[i].split(':'); 5434 let time = SBF.convertUTCDateToLocalDate(`01/01/2000 ${hm[0]}:${hm[1]}`, utc_offset); 5435 code += time.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) + (i == 0 || i == 2 ? `<span>${sb_('to')}</span>` : i == 1 && times[i + 1] ? `<br />` : ''); 5436 } 5437 } 5438 table.find(' > div > span').html(`<i class="sb-icon-clock"></i> ${sb_('Time zone')} ${Intl.DateTimeFormat().resolvedOptions().timeZone}`); 5439 $(this).html(code); 5440 }); 5441 return table.html(); 5442 }, 5443 5444 // Slider 5445 sliderChange: function (id, direction = 'left') { 5446 let slider = chat.find(`#${id}`); 5447 if (slider.length && !slider.hasClass('sb-moving')) { 5448 let items = slider.find('.sb-slider > div > div'); 5449 let item = items.eq(0); 5450 let width = Math.ceil(item.closest('.sb-slider').width()); 5451 let negative = (direction == 'right' ? -1 : 1); 5452 let margin = parseFloat(parseFloat(parseFloat(item.css('margin-left')) + (width * negative))); 5453 let check = width * (items.length - 1) * -1; 5454 if (margin < 1 && margin >= check) { 5455 item.css('margin-left', margin + 'px'); 5456 slider.addClass('sb-moving'); 5457 setTimeout(() => { slider.removeClass('sb-moving'); }, 1200); 5458 } 5459 slider.find('.sb-icon-arrow-right').sbActive(!(check > (margin - 15) && check < (margin + 15))); 5460 slider.find('.sb-icon-arrow-left').sbActive(margin < -10); 5461 } 5462 }, 5463 5464 iframe: { 5465 script_loaded: false, 5466 5467 load: function (action, url, title, message_id) { 5468 url = url.split('|'); 5469 if (SBRichMessages.iframe.script_loaded) { 5470 if (action == 'calendly') { 5471 this.load_(action, url[0], title); 5472 } else { 5473 chat_overlay_panel.sbActive(true).attr('data-id', action); 5474 } 5475 } else { 5476 let actions = { calendly: ['https://assets.calendly.com/assets/external/widget.js'], tidycal: ['https://asset-tidycal.b-cdn.net/js/embed.js'] }[action]; 5477 if (action == 'tidycal') { 5478 this.load_(action, url[0], title); 5479 } 5480 SBF.loadResource(actions[0], true, () => { 5481 SBRichMessages.iframe.script_loaded = true; 5482 window.addEventListener('message', function (e) { 5483 console.log(e); 5484 if ((action == 'calendly' && e.origin === 'https://calendly.com' && e.data.event == 'calendly.event_scheduled') || (action == 'tidycal' && e.origin === 'https://tidycal.com' && e.data.data && e.data.data.booking)) { 5485 SBChat.updateMessage(message_id, sb_(url[1] ? url[1] : 'Booking completed.')); 5486 chat_overlay_panel.sbActive(false); 5487 } 5488 }); 5489 if (action == 'calendly') { 5490 this.load_(action, url[0], title); 5491 } 5492 }); 5493 } 5494 }, 5495 5496 load_: function (action, url, title) { 5497 if (action == 'calendly') { 5498 Calendly.initInlineWidget({ 5499 url: (url.includes('http') ? '' : 'https://') + url + '?hide_landing_page_details=1&hide_event_type_details=1&hide_gdpr_banner=1', 5500 parentElement: chat_overlay_panel.find('> div').eq(1), 5501 prefil: activeUser().type == 'user' ? { name: activeUser().name, email: activeUser().email } : {} 5502 }); 5503 } 5504 if (action == 'tidycal') { 5505 chat_overlay_panel.find('> div').eq(1).html(`<div class="sb-scroll-area"><div class="tidycal-embed" data-path="${url.replace('https://tidycal.com/', '')}"></div></div>`); 5506 } 5507 chat_overlay_panel.find('> div:first-child > div').html(sb_(title)); 5508 chat_overlay_panel.sbActive(true).attr('data-id', action); 5509 } 5510 }, 5511 5512 rating: function (is_close_chat = false) { 5513 chat_overlay_panel.find('> div:first-child > div').html(sb_(CHAT_SETTINGS.rating_title)); 5514 chat_overlay_panel.find('> div:last-child').html(`<div class="sb-text">${sb_(CHAT_SETTINGS.rating_text)}</div>${CHAT_SETTINGS.rating_message ? `<div class="sb-input sb-input-textarea"><textarea placeholder="${sb_('Add a message here...')}"></textarea></div>` : ''}<div class="sb-rating"><div><i data-rating="positive" class="sb-submit sb-icon-like"><span>${sb_('Helpful')}</span></i><i data-rating="negative" class="sb-submit sb-icon-dislike"><span>${sb_('Not helpful')}</span></i></div></div>`); 5515 chat_overlay_panel.sbActive(true).attr('data-id', 'rating').attr('data-close-chat', is_close_chat ? 'true' : ''); 5516 }, 5517 5518 isShortcode: function (shortcode_name) { 5519 return shortcode_name in this.rich_messsages || (admin && SB_ADMIN_SETTINGS.rich_messages.includes(shortcode_name)) || (!admin && CHAT_SETTINGS.rich_messages.includes(shortcode_name)); 5520 } 5521 } 5522 window.SBRichMessages = SBRichMessages; 5523 5524 /* 5525 * ---------------------------------------------------------- 5526 * FORM METHODS 5527 * ---------------------------------------------------------- 5528 */ 5529 5530 var SBForm = { 5531 5532 // Get all settings 5533 getAll: function (area) { 5534 let settings = {}; 5535 $(area).find('.sb-input[id]').each((i, element) => { 5536 settings[$(element).attr('id')] = this.get(element); 5537 }); 5538 return settings; 5539 }, 5540 5541 // Get a single setting 5542 get: function (input) { 5543 input = $(input); 5544 let type = input.data('type'); 5545 let name = sb_(SBF.escape(input.find(' > span').html())); 5546 switch (type) { 5547 case 'image': 5548 let url = input.find('.image').attr('data-value'); 5549 return [SBF.null(url) ? '' : url, name]; 5550 case 'select': 5551 return [SBF.escape(input.find('select').val()), name]; 5552 case 'select-input': 5553 let select = input.find('select,input[disabled]'); 5554 return [SBF.escape((select.is('select') || select.is('input') ? select.val() : (input.find('.sb-select').length ? input.find('.sb-select > p').attr('data-value') : input.find('> div').html())) + input.find('> input').val()), name]; 5555 default: 5556 let target = input.find('input'); 5557 return [SBF.escape(target.length ? target.val() : input.find('[data-value]').attr('data-value')), name]; 5558 } 5559 }, 5560 5561 // Set a single setting 5562 set: function (item, value) { 5563 item = $(item); 5564 if (item.length) { 5565 let type = item.data('type'); 5566 switch (type) { 5567 case 'image': 5568 if (value) { 5569 item.find('.image').attr('data-value', value).css('background-image', `url("${value}")`); 5570 } else { 5571 item.find('.image').removeAttr('data-value').removeAttr('style'); 5572 } 5573 break; 5574 case 'select': 5575 item.find('select').val(value); 5576 break; 5577 default: 5578 item.find('input,textarea').val(value); 5579 break; 5580 } 5581 return true; 5582 } 5583 return false;
5584 }, 5585 5586 // Clear all the input values 5587 clear: function (area) { 5588 $(area).find('.sb-input,.sb-setting').each((i, element) => { 5589 this.set(element, ''); 5590 $(element).find('input, select, textarea').removeClass('sb-error'); 5591 }); 5592 this.set($(area).find('#user_type'), 'user'); 5593 }, 5594 5595 // Check for errors on user input 5596 errors: function (area) { 5597 let errors = false; 5598 let items = $(area).find('input, select, textarea').removeClass('sb-error'); 5599 items.each(function (i) { 5600 let value = $.trim($(this).val()); 5601 let type = $(this).attr('type'); 5602 let required = $(this).prop('required'); 5603 if ((required && !value) || ((required || value) && ((type == 'password' && (value.length < 8 || (items.length > (i + 1) && items.eq(i + 1).attr('type') == 'password' && items.eq(i + 1).val() != value))) || (type == 'email' && (!value.includes('@') || !value.includes('.') || /;|:|\/|\\|,|#|"|!|=|\*|{|}|[|]|£|\$|â¬|~|'|>|<|\^|&/.test(value))) || ($(this).attr('data-phone') && value && ((!$(this).parent().find('select').val() && !$(this).prev().find('> div > p').attr('data-value') && !$(this).parent().find('div').html().trim().startsWith('+')) || isNaN(value) || value.includes('+') || value.length < 5))))) { 5604 errors = true; 5605 $(this).addClass('sb-error'); 5606 } 5607 }); 5608 items = $(area).find('[data-required]').removeClass('sb-error'); 5609 items.each(function () { 5610 if (SBF.null($(this).attr('data-value'))) { 5611 $(this).addClass('sb-error'); 5612 errors = true; 5613 } 5614 }); 5615 return errors; 5616 }, 5617 5618 // Display a error message 5619 showErrorMessage: function (area, message) { 5620 $(area).find('.sb-info').html(sb_(message)).sbActive(true); 5621 clearTimeout(timeout); 5622 timeout = setTimeout(function () { 5623 $(area).find('.sb-info').sbActive(false); 5624 }, 7000); 5625 }, 5626 5627 // Display a success message 5628 showSuccessMessage: function (area, message) { 5629 $(area).find('.sb-info').remove(); 5630 $(area).addClass('sb-success').find('.sb-content').html(`<div class="sb-text">${message}</div>`); 5631 }, 5632 5633 // Return the registration error message 5634 getRegistrationErrorMessage(area_or_response, type = 'validation') { 5635 let error_text = ''; 5636 if (type == 'response') { 5637 return SBF.errorValidation(area_or_response, 'duplicate-email') ? 'This email is already in use. Please use another email.' : (SBF.errorValidation(area_or_response, 'duplicate-phone') ? 'This phone number is already in use. Please use another number.' : (SBF.errorValidation(area_or_response, 'invalid-envato-purchase-code') || SBF.errorValidation(area_or_response, 'invalid-boxcoin-license-key') ? 'Invalid license key.' : (SBF.errorValidation(area_or_response, 'invalid-otp') ? 'Invalid one-time code.' : 'Error. Please check your information and try again.'))) 5638 } 5639 $(area_or_response).find('.sb-input:not(#password-check) [required]').each(function () { 5640 error_text += ', ' + $(this).closest('.sb-input').find('span').html() + ($(this).attr('type') == 'password' ? ' (' + sb_('8 characters minimum') + ')' : ''); 5641 }); 5642 return `${error_text.substring(2)} ${sb_((error_text.includes(',') ? 'are' : 'is') + ' required.')}`; 5643 } 5644 } 5645 window.SBForm = SBForm; 5646 5647 /* 5648 * ---------------------------------------------------------- 5649 * APPS 5650 * ---------------------------------------------------------- 5651 */ 5652 5653 var SBApps = { 5654 5655 // Get the login data 5656 login: function () { 5657 if (typeof SB_DEFAULT_USER != ND && SB_DEFAULT_USER) { 5658 return [SB_DEFAULT_USER, 'default']; 5659 } 5660 if (this.is('wp') && typeof SB_WP_ACTIVE_USER != ND && CHAT_SETTINGS.wp_users_system == 'wp') { 5661 return [[SB_WP_ACTIVE_USER, typeof SB_WP_AVATAR != ND ? SB_WP_AVATAR : ''], 'wp']; 5662 }
5663 if (typeof SB_SHOPIFY_ACTIVE_USER != ND) { 5664 return [SB_SHOPIFY_ACTIVE_USER, 'shopify']; 5665 } 5666 if (typeof SB_PERFEX_ACTIVE_USER != ND) { 5667 return [[SB_PERFEX_ACTIVE_USER, SB_PERFEX_CONTACT_ID], 'perfex']; 5668 } 5669 if (typeof SB_WHMCS_ACTIVE_USER != ND) { 5670 return [SB_WHMCS_ACTIVE_USER, 'whmcs']; 5671 } 5672 if (typeof SB_AECOMMERCE_ACTIVE_USER != ND) { 5673 return [SB_AECOMMERCE_ACTIVE_USER, 'aecommerce']; 5674 } 5675 return false; 5676 }, 5677 5678 // Check if an app is installed and active 5679 is: function (name) { 5680 if (admin) return SBAdmin.apps.is(name); 5681 if (name == 'wordpress' || name == 'wp') return CHAT_SETTINGS.wp; 5682 return name in CHAT_SETTINGS ? CHAT_SETTINGS[name] : false; 5683 }, 5684 5685 shopify: { 5686 5687 startCartSynchronization: function () { 5688 setInterval(() => { 5689 if (activeUser()) { 5690 fetch('/cart.js').then((response) => response.json()).then((cart) => { 5691 cart = { 5692 total: cart.total_price, 5693 currency: cart.currency, 5694 items: cart.items.map(item => ({ 5695 id: item.product_id, 5696 price: item.price, 5697 handle: item.handle, 5698 title: item.title, 5699 quantity: item.quantity 5700 })) 5701 }; 5702 let cart_string = JSON.stringify(cart); 5703 if (storage('shopify-cart') != cart_string) { 5704 storage('shopify-cart', cart_string); 5705 SBF.ajax({ 5706 function: 'shopify-cart-sync', 5707 cart: cart 5708 }); 5709 } 5710 }); 5711 } 5712 }, 10000); 5713 } 5714 }, 5715 5716 wordpress: { 5717 5718 ajax: function (action, data, onSuccess = false) { 5719 if (typeof SB_WP_AJAX_URL == ND) { 5720 return onSuccess ? onSuccess(false) : false; 5721 }; 5722 $.ajax({ 5723 method: 'POST', 5724 url: SB_WP_AJAX_URL, 5725 data: $.extend({ action: 'sb_wp_ajax', type: action }, data) 5726 }).done((response) => { 5727 if (onSuccess) { 5728 onSuccess(response); 5729 } 5730 }); 5731 } 5732 }, 5733 5734 dialogflow: { 5735 typing_enabled: true, 5736 project_id: false, 5737 busy: false, 5738 5739 message: function (message = '', attachments = [], audio = false) { 5740 if (message.length < 2 && !attachments.length) { 5741 return; 5742 } 5743 if (!audio) { 5744 for (var i = 0; i < attachments.length; i++) { 5745 if (attachments[i][0].includes('voice_message')) { 5746 audio = attachments[i][1]; 5747 } 5748 } 5749 } 5750 if (!$('.sb-emoji-list').html().includes('<li>' + message + '</li>')) { 5751 this.openAI(message, false, audio, attachments); 5752 } 5753 }, 5754 5755 openAI: function (message, onSuccess = false, audio = false, attachments = []) { 5756 if (CHAT_SETTINGS.is_chatbot_active) { 5757 SBChat.headerAgent(true); 5758 if (SBChat.agent_id == bot_id && (!SBApps.dialogflow.humanTakeoverActive() || this.typing_enabled)) { 5759 this.typing(); 5760 } 5761 if (this.chatbotLimit()) { 5762 return SBChat.sendMessage(bot_id, this.chatbotLimit()); 5763 } 5764 setTimeout(() => { 5765 SBF.ajax({ 5766 function: 'open-ai-message', 5767 message: message, 5768 conversation_id: SBChat.conversation ? SBChat.conversation.id : false, 5769 audio: audio, 5770 extra: { token: SBApps.dialogflow.token }, 5771 attachments: attachments, 5772 context: CHAT_SETTINGS.open_ai_context_awareness ? document.title.toUpperCase() + '\n\n\n' + $('meta[name="description"]').attr('content') || '' : false 5773 }, (response) => { 5774 this.busy = false;
5775 SBChat.typing(-1, 'stop'); 5776 SBF.event('SBOpenAIMessage', { response: response, message: message }); 5777 if (response && (response[0] || response[5])) { 5778 if (CHAT_SETTINGS.chatbot_limit) { 5779 this.chatbot_limit++; 5780 } 5781 if (CHAT_SETTINGS.slack_active && message && (!dialogflow_human_takeover || SBApps.dialogflow.humanTakeoverActive())) { 5782 SBApps.slack.message(activeUser().id, CHAT_SETTINGS.bot_name, CHAT_SETTINGS.bot_image, response[1]); 5783 } 5784 if (response[3]) { 5785 SBChat.offlineMessage(); 5786 SBChat.followUp(); 5787 } 5788 5789 // Payload 5790 if (response[5]) { 5791 if (response[5].redirect) { 5792 setTimeout(() => { 5793 document.location = response[5].redirect; 5794 }, 500); 5795 } 5796 if (response[5].open_article) { 5797 SBChat.showArticles(response[5].open_article); 5798 } 5799 if (response[5].event == 'conversation-status-update-3') { 5800 SBChat.conversationArchived(); 5801 } 5802 if (response[5].login_cookie) { 5803 SBF.loginCookie(response[5].login_cookie[1]); 5804 activeUser(new SBUser(response[5].login_cookie[0])); 5805 } 5806 } 5807 this.chatbotLimit(false); 5808 } else if (response[1] !== false) { 5809 SBF.error(response[1].error ? response[1].error.message : response[1], 'SBApps.dialogflow.openAI'); 5810 } 5811 if (onSuccess) { 5812 onSuccess(response); 5813 } 5814 }); 5815 this.busy = true; 5816 }, CHAT_SETTINGS.bot_delay == 0 ? 2000 : parseInt(CHAT_SETTINGS.bot_delay) * 1000); 5817 } 5818 }, 5819 5820 flowOnLoad() { 5821 if (CHAT_SETTINGS.flow_on_load && !storage('flow-on-load') && activeUser()) { 5822 if (SBChat.conversation) { 5823 SBF.ajax({ 5824 function: 'run-flow-on-load', 5825 message: CHAT_SETTINGS.flow_on_load, 5826 conversation_id: SBChat.conversation.id 5827 }); 5828 } else { 5829 SBChat.newConversation(3, activeUser().id, '', [], null, null, () => { 5830 SBF.ajax({ 5831 function: 'run-flow-on-load', 5832 message: CHAT_SETTINGS.flow_on_load, 5833 conversation_id: SBChat.conversation.id 5834 }); 5835 }); 5836 } 5837 storage('flow-on-load', true); 5838 } 5839 }, 5840 5841 typing: function () { 5842 clearTimeout(timeout_typing); 5843 timeout_typing = setTimeout(() => { 5844 SBChat.typing(-1, 'start'); 5845 }, 1000); 5846 }, 5847 5848 active: function (active = true) { 5849 if (active === false) { 5850 SBChat.conversation.set('is_human_takeover', true); 5851 return false; 5852 } 5853 if (active == 'activate') { 5854 SBChat.conversation.set('is_human_takeover', false); 5855 } 5856 if (!admin && SBApps.dialogflow.humanTakeoverActive() && CHAT_SETTINGS.dialogflow_human_takeover_disable_chatbot) { 5857 return false;
5858 } 5859 return CHAT_SETTINGS.is_chatbot_active && !admin && (!SBChat.conversation || !SBApps.dialogflow.humanTakeoverActive() || !SBChat.agent_online) && (!CHAT_SETTINGS.dialogflow_office_hours || !CHAT_SETTINGS.office_hours); 5860 }, 5861 5862 humanTakeover: function () { 5863 SBF.ajax({ 5864 function: 'human-takeover', 5865 conversation_id: SBChat.conversation.id 5866 }, () => { 5867 SBChat.offlineMessage(); 5868 SBChat.followUp(); 5869 this.active(false); 5870 if (CHAT_SETTINGS.queue_human_takeover) { 5871 CHAT_SETTINGS.queue = true; 5872 SBChat.queue(SBChat.conversation.id); 5873 } 5874 }); 5875 }, 5876 5877 humanTakeoverActive: function () { 5878 return SBChat.conversation ? SBChat.conversation.get('is_human_takeover') : false; 5879 }, 5880 5881 translate: function (strings, language_code, onSuccess, message_ids, conversation_id) { 5882 SBF.ajax({ 5883 function: 'google-translate', 5884 strings: strings, 5885 language_code: language_code, 5886 message_ids: message_ids, 5887 conversation_id: conversation_id 5888 }, (response) => { 5889 onSuccess(response); 5890 }); 5891 }, 5892 5893 chatbotLimit: function (check = true) { 5894 if (CHAT_SETTINGS.chatbot_limit) { 5895 let chatbot_limit = storage('chatbot_limit'); 5896 let now = (new Date()).getTime() / 1000; 5897 if (!chatbot_limit) { 5898 chatbot_limit = []; 5899 } 5900 if (check) { 5901 let interval = now - CHAT_SETTINGS.chatbot_limit.interval; 5902 let chatbot_limit_new = []; 5903 for (var i = 0; i < chatbot_limit.length; i++) { 5904 if (chatbot_limit[i] > interval) { 5905 chatbot_limit_new.push(chatbot_limit[i]); 5906 } 5907 } 5908 storage('chatbot_limit', chatbot_limit_new); 5909 if (chatbot_limit_new.length >= CHAT_SETTINGS.chatbot_limit.quota) { 5910 return CHAT_SETTINGS.chatbot_limit.message; 5911 } 5912 } else { 5913 chatbot_limit.push(now); 5914 storage('chatbot_limit', chatbot_limit); 5915 } 5916 } 5917 return false; 5918 } 5919 }, 5920 5921 woocommerce: { 5922 5923 // Update the cart 5924 updateCart: function (action, product_id, onSuccess = false) { 5925 SBApps.wordpress.ajax(action, { product_id: product_id }, onSuccess); 5926 }, 5927 5928 // Waiting list 5929 waitingList: function (action = 'request', product_id = false) { 5930 if (typeof SB_WP_WAITING_LIST !== ND && (action != 'request' || SB_WP_WAITING_LIST && SBF.storageTime('waiting-list-' + SB_WP_PAGE_ID, 24)) && activeUser()) { 5931 SBF.ajax({ 5932 function: 'woocommerce-waiting-list', 5933 product_id: product_id === false ? SB_WP_PAGE_ID : product_id, 5934 conversation_id: SBChat.conversation.id, 5935 action: action, 5936 token: this.token 5937 }, (response) => { 5938 if (response) { 5939 SBF.storageTime('waiting-list-' + SB_WP_PAGE_ID); 5940 if (action == 'request' && (!SBChat.chat_open || SBChat.dashboard)) { 5941 SBChat.updateConversations(); 5942 } 5943 } 5944 }); 5945 } 5946 } 5947 }, 5948 5949 aecommerce: { 5950 5951 cart: function () { 5952 if (SBApps.is('aecommerce') && typeof SB_AECOMMERCE_CART != ND && typeof SB_AECOMMERCE_ACTIVE_USER != ND && storage('aecommerce') != JSON.stringify(SB_AECOMMERCE_CART)) { 5953 SBF.ajax({ 5954 function: 'aecommerce-cart', 5955 cart: SB_AECOMMERCE_CART 5956 }, () => { 5957 storage('aecommerce', JSON.stringify(SB_AECOMMERCE_CART)); 5958 }); 5959 } 5960 } 5961 }, 5962 5963 martfury: { 5964 5965 privateChat: function () { 5966 let store = $('#tab-vendor > h4,.ps-product__vendor > a'); 5967 if (store.length) { 5968 store = store.html().toLowerCase(); 5969 for (var i = 0; i < CHAT_SETTINGS.martfury.length; i++) { 5970 if (store == CHAT_SETTINGS.martfury[i]['martfury-linking-store'].toLowerCase()) { 5971 let agent_id = CHAT_SETTINGS.martfury[i]['martfury-linking-agent']; 5972 let conversations = SBF.activeUser() ? SBF.activeUser().conversations : []; 5973 SBChat.default_agent = agent_id; 5974 for (var j = 0; j < conversations.length; j++) { 5975 if (conversations[j].get('agent_id') == agent_id) { 5976 SBChat.openConversation(conversations[j].id); 5977 return; 5978 } 5979 } 5980 SBChat.clear(); 5981 SBChat.hideDashboard(); 5982 } 5983 } 5984 } 5985 } 5986 }, 5987 5988 whatsapp: { 5989 5990 call: { 5991 5992 answer: function (conversation_id, onSuccess) { 5993 const incoming_call = admin ? SBAdmin.conversations.active_calls[conversation_id] : call_data; 5994 if (!incoming_call) { 5995 return; 5996 } 5997 SBF.call.initRTC((answer) => { 5998 SBF.ajax({ 5999 function: 'whatsapp-call-answer', 6000 phone_number_id: incoming_call.phone_number_id, 6001 call_id: incoming_call.id, 6002 sdp_answer: answer.sdp 6003 }, () => { 6004 SBF.ajax({ 6005 function: 'whatsapp-call-answer', 6006 phone_number_id: incoming_call.phone_number_id, 6007 call_id: incoming_call.id, 6008 sdp_answer: answer.sdp, 6009 is_accept: true 6010 }, (response) => { 6011 onSuccess(response); 6012 }); 6013 }); 6014 }, incoming_call.session.sdp); 6015 }, 6016 6017 decline: function (conversation_id, is_terminate, onSuccess) { 6018 const incoming_call = admin ? SBAdmin.conversations.active_calls[conversation_id] : call_data; 6019 if (!incoming_call) { 6020 return; 6021 } 6022 SBF.ajax({ 6023 function: 'whatsapp-call-decline', 6024 phone_number_id: incoming_call.phone_number_id, 6025 call_id: incoming_call.id, 6026 is_terminate: is_terminate 6027 }, (response) => { 6028 onSuccess(response); 6029 }); 6030 }, 6031 6032 start: function (sdp, user_id, onSuccess) { 6033 SBF.ajax({ 6034 function: 'whatsapp-call-start', 6035 sdp: sdp, 6036 user_id: user_id 6037 }, (response) => { 6038 if (response.error) { 6039 SBF.error(response.error.message, 'SBApps.whatsapp.call.start'); 6040 } else if (response == 'request-sent' && admin) { 6041 SBAdmin.infoPanel('Call permission request sent successfully. The user will receive a message asking for consent to receive voice calls.'); 6042 } else { 6043 call_data = response; 6044 } 6045 onSuccess(response); 6046 }); 6047 } 6048 } 6049 }, 6050 6051 slack: { 6052 slack_channel: [-1, -1], 6053 6054 message: function (user_id, full_name, profile_image, message, attachments = []) { 6055 if (!SBChat.conversation || (!message && !attachments.length)) { 6056 return false;
6057 } 6058 let conversation_id = SBChat.conversation.id; 6059 SBF.ajax({ 6060 function: 'send-slack-message', 6061 user_id: user_id, 6062 full_name: full_name, 6063 profile_image: profile_image, 6064 conversation_id: conversation_id, 6065 message: message, 6066 attachments: attachments, 6067 channel: this.slack_channel[0] == activeUser().id ? this.slack_channel[1] : false 6068 }, (response) => { 6069 this.slack_channel = [activeUser().id, response[1]]; 6070 SBF.event('SBSlackMessageSent', { message: message, conversation_id: conversation_id, slack_channel: response[1] }); 6071 }); 6072 } 6073 } 6074 } 6075 window.SBApps = SBApps; 6076 6077 /* 6078 * ---------------------------------------------------------- 6079 * INIT 6080 * ---------------------------------------------------------- 6081 */ 6082 6083 $(document).ready(function () { 6084 main = $('.sb-admin, .sb-admin-start'); 6085 if (main.length) { 6086 admin = true; 6087 initialize(); 6088 return; 6089 } 6090 let url_full; 6091 let url; 6092 let init = false; 6093 if (typeof SB_INIT_URL != ND) { 6094 if (!SB_INIT_URL.includes('.js')) { 6095 SB_INIT_URL += '/js/main.js?v=' + version; 6096 } 6097 url_full = SB_INIT_URL; 6098 } else { 6099 let scripts = document.getElementsByTagName('script'); 6100 let checks = ['init.js', 'main.js', 'min/init.min.js', 'min/main.min.js']; 6101 for (var i = 0; i < scripts.length; i++) { 6102 let source = scripts[i].src; 6103 if (scripts[i].id == 'sbinit') { 6104 url_full = source; 6105 init = init ? init : url_full.includes('init.'); 6106 break; 6107 } else { 6108 for (var j = 0; j < checks.length; j++) { 6109 if (source && source.includes('/supportboard/js/' + checks[j])) { 6110 url_full = source; 6111 init = init ? init : url_full.includes('init.'); 6112 break; 6113 } 6114 } 6115 } 6116 } 6117 } 6118 let parameters = SBF.getURL(false, url_full); 6119 if (parameters.url) { 6120 url_full = parameters.url; 6121 } 6122 if (typeof SB_DISABLED != ND && SB_DISABLED) { 6123 return; 6124 } 6125 if (init) { 6126 initialize(); 6127 return; 6128 } 6129 if (typeof SB_TICKETS != ND || (parameters.mode == 'tickets')) { 6130 tickets = true; 6131 parameters.mode = 'tickets'; 6132 } 6133 if (parameters.cloud) { 6134 cloud_data = parameters.cloud; 6135 } 6136 let lang_optional = is_shopify && Shopify.locale != 'en' ? Shopify.locale : false; 6137 let min = url_full.lastIndexOf('main.min.js'); 6138 url = url_full.substr(0, url_full.lastIndexOf('main.js') > 0 ? (url_full.lastIndexOf('main.js') - 4) : (min - 8)); 6139 let url_chat = url + '/include/init.php' + (parameters.lang ? '?lang=' + parameters.lang : '') + (lang_optional ? '?lang_optional=' + lang_optional : '') + (parameters.mode ? '&mode=' + parameters.mode : '') + (cloud_data ? '&cloud=' + cloud_data : ''); 6140 SBF.cors('GET', url_chat.replace('.php&', '.php?'), (response) => { 6141 let target = 'body'; 6142 if (tickets && $('#sb-tickets').length) { 6143 target = '#sb-tickets'; 6144 } 6145 $(target).append(response); 6146 SBF.loadResource(url + '/css/' + (tickets ? 'tickets' : 'main') + '.css'); 6147 if (tickets) { 6148 SBF.loadResource(url + '/apps/tickets/tickets' + (min > 0 ? '.min' : '') + '.js?v=' + version, true, () => { 6149 initialize(); 6150 }); 6151 } else { 6152 initialize(); 6153 } 6154 if (parameters.lang) { 6155 SB_LANG = [parameters.lang, admin ? 'admin' : 'front']; 6156 } 6157 }); 6158 }); 6159 6160 function initialize() { 6161 main = $('.sb-admin, .sb-chat, .sb-tickets'); 6162 6163 // Initialize the chat and the user 6164 if (main.length && typeof SB_AJAX_URL != ND) { 6165 chat = main.find('.sb-list').eq(0); 6166 chat_editor = main.find('.sb-editor'); 6167 chat_textarea = chat_editor.find('textarea'); 6168 chat_scroll_area = main.find(admin || tickets ? '.sb-list' : '> div > .sb-scroll-area'); 6169 chat_header = chat_scroll_area.find('.sb-header'); 6170 chat_emoji = chat_editor.find('.sb-emoji'); 6171 chat_overlay_panel = main.find('.sb-overlay-panel'); 6172 chat_status = tickets ? main.find('.sb-profile-agent .sb-status') : null; 6173 SBChat.enabledAutoExpand(); 6174 SBChat.audio = main.find('#sb-audio').get(0); 6175 SBChat.label_date = main.find('.sb-label-date-top'); 6176 6177 // Check if cookies works 6178 SBF.cookie('sb-check', 'ok', 1, 'set'); 6179 if (SBF.cookie('sb-check') != 'ok') { 6180 cookies_supported = false;
6181 console.warn('Support Board: cookies not available.'); 6182 } else { 6183 SBF.cookie('sb-check', false, false, false); 6184 } 6185 if (!admin) { 6186 SBF.ajax({ 6187 function: 'get-front-settings', 6188 tickets: tickets, 6189 popup: !storage('popup') && !tickets 6190 }, (response) => { 6191 CHAT_SETTINGS = response; 6192 if (typeof SB_LOCAL_SETTINGS != ND) { 6193 $.extend(CHAT_SETTINGS, SB_LOCAL_SETTINGS); 6194 } 6195 bot_id = CHAT_SETTINGS.bot_id; 6196 dialogflow_human_takeover = CHAT_SETTINGS.dialogflow_human_takeover; 6197 agents_online = CHAT_SETTINGS.agents_online; 6198 SBPusher.active = CHAT_SETTINGS.pusher; 6199 if (CHAT_SETTINGS.language) { 6200 SB_LANG = CHAT_SETTINGS.language != 'auto' ? [CHAT_SETTINGS.language, 'front'] : (is_shopify && Shopify.locale != 'en' ? Shopify.locale : false); 6201 } 6202 if (typeof SB_REGISTRATION_REQUIRED != ND) { 6203 CHAT_SETTINGS.registration_required = SB_REGISTRATION_REQUIRED; 6204 CHAT_SETTINGS.tickets_registration_required = SB_REGISTRATION_REQUIRED; 6205 } 6206 if (typeof SB_ARTICLES_PAGE != ND && SB_ARTICLES_PAGE) { 6207 SBChat.automations.runAll(); 6208 SBChat.initArticlesPage(); 6209 }
6210 if (typeof SB_ARTICLE_ID != ND) { 6211 SBChat.initSingleArticlePage(); 6212 } 6213 if ((!tickets || !CHAT_SETTINGS.tickets_manual_init) && ((tickets && !CHAT_SETTINGS.tickets_manual_init) || (!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())))) { 6214 SBChat.initChat(); 6215 } 6216 if (CHAT_SETTINGS.cron) { 6217 setTimeout(function () { 6218 SBF.ajax({ function: 'cron-jobs' }); 6219 }, 10000); 6220 } 6221 if (CHAT_SETTINGS.cron_email_piping) { 6222 setTimeout(function () { 6223 SBF.ajax({ function: 'email-piping' }); 6224 }, 8000); 6225 } 6226 if (CHAT_SETTINGS.push_notifications_users) { 6227 SBF.serviceWorker.init(); 6228 } 6229 if (tickets) { 6230 if (CHAT_SETTINGS.tickets_default_department) { 6231 SBChat.default_department = CHAT_SETTINGS.tickets_default_department; 6232 } 6233 if (CHAT_SETTINGS.dialogflow_disable_tickets) { 6234 CHAT_SETTINGS.open_ai_active = false; 6235 } 6236 } 6237 if (SBApps.is('martfury')) { 6238 let session = false; 6239 setInterval(function () { 6240 if (activeUser()) { 6241 let current = SBF.cookie('XSRF-TOKEN'); 6242 if (current && current != session) { 6243 SBF.ajax({ function: 'martfury-session' }); 6244 session = current; 6245 } 6246 } 6247 }, 3000); 6248 } 6249 if (is_shopify) { 6250 SBApps.shopify.startCartSynchronization(); 6251 main.addClass('sb-shopify'); 6252 } 6253 setAudio(); 6254 SBApps.aecommerce.cart(); 6255 setTimeout(() => { 6256 SBF.event('SBReady'); 6257 }, 500); 6258 }); 6259 } else { 6260 setAudio(); 6261 SBF.event('SBReady'); 6262 } 6263 6264 $(chat_editor).on('keydown', 'textarea', function (e) { 6265 if (!e) return; 6266 if (e.which == 13 && (!tickets || CHAT_SETTINGS.tickets_enter_button) && !mobile && !e.ctrlKey && !e.shiftKey) { 6267 SBChat.submit(); 6268 e.preventDefault; 6269 return false; 6270 } 6271 if (admin && e.which == 13 && e.ctrlKey) { 6272 SBChat.insertText('\n'); 6273 } 6274 }); 6275 6276 $(main).on('keydown', '.sb-dashboard-articles input', function (e) { 6277 if (e.which == 13) { 6278 $(this).next().click(); 6279 } 6280 }); 6281 6282 if (typeof SB_DEFAULT_DEPARTMENT !== ND) { 6283 SBChat.default_department = SB_DEFAULT_DEPARTMENT; 6284 } 6285 if (typeof SB_DEFAULT_AGENT !== ND) { 6286 SBChat.default_agent = SB_DEFAULT_AGENT; 6287 } 6288 if (typeof SB_DEFAULT_TAGS !== ND) { 6289 SBChat.default_tags = SB_DEFAULT_TAGS; 6290 } 6291 if (SBF.storage('call-active')) { 6292 if (admin) { 6293 SBAdmin.conversations.active_calls[SBF.storage('call-active')[0]] = SBF.storage('call-active')[1]; 6294 } else { 6295 call_data = SBF.storage('call-active')[1]; 6296 } 6297 SBF.call.decline(true, SBF.storage('call-active')[0], () => { 6298 setTimeout(() => { 6299 if (admin) { 6300 SBAdmin.conversations.update(); 6301 } else {
6302 SBChat.update(); 6303 } 6304 }, 1000); 6305 }); 6306 SBF.storage('call-active', false); 6307 } 6308 } else { 6309 SBF.event('SBReady'); 6310 } 6311 6312 // Image from clipboard 6313 document.onpaste = function (pasteEvent) { 6314 let item = pasteEvent.clipboardData.items[0]; 6315 if (item.type.indexOf('image') === 0) { 6316 var reader = new FileReader(); 6317 reader.onload = function (event) { 6318 let data = event.target.result.split(',') 6319 let bytes = data[0].indexOf('base64') >= 0 ? atob(data[1]) : decodeURI(data[1]) 6320 let ia = new Uint8Array(bytes.length) 6321 for (let i = 0; i < bytes.length; i++) { 6322 ia[i] = bytes.charCodeAt(i) 6323 } 6324 let form = new FormData(); 6325 form.append('file', new Blob([ia], { type: data[0].split(':')[1].split(';')[0] }), 'image_print.jpg'); 6326 SBF.upload(form, function (response) { SBChat.uploadResponse(response) }); 6327 }; 6328 reader.readAsDataURL(item.getAsFile()); 6329 } 6330 } 6331 6332 // Disable real-time if browser tab not active 6333 document.addEventListener('visibilitychange', function () { 6334 SBF.visibilityChange(document.visibilityState); 6335 }, false); 6336 6337 $(main).on('click', function () { 6338 if (!SBChat.tab_active) { 6339 SBF.visibilityChange(); 6340 } 6341 }); 6342 6343 // Set the global container for both admin and front 6344 global = main; 6345 if (admin) { 6346 main = main.find('.sb-conversation'); 6347 } 6348 call_bar = global.find('#sb-call-bar'); 6349 6350 // Scroll detection 6351 $(chat_scroll_area).on('scroll', function () { 6352 let scroll = chat_scroll_area.scrollTop(); 6353 let count = label_date_items.length; 6354 SBChat.scrollHeader(); 6355 if (!SBChat.label_date_show && count && label_date_items[count - 1].getBoundingClientRect().top > (chat[0].getBoundingClientRect().top + chat.outerHeight())) { 6356 SBChat.label_date_show = true; 6357 if (count > 1) { 6358 SBChat.label_date.html($(label_date_items[count - 2]).html()); 6359 } 6360 } 6361 if (SBChat.label_date_show) { 6362 SBChat.label_date.sbActive(true); 6363 clearTimeout(label_date_timeout[0]); 6364 label_date_timeout[0] = setTimeout(() => { SBChat.label_date.sbActive(false) }, 1500); 6365 } 6366 if (count) { 6367 if (SBChat.isBottom()) { 6368 SBChat.label_date.html($(label_date_items[count - 1]).html()); 6369 SBChat.label_date_show = label_date_items.length && label_date_items[label_date_items.length - 1].getBoundingClientRect().top < 0; 6370 } else { 6371 let label_date_top = SBChat.label_date[0].getBoundingClientRect().top; 6372 for (var i = 0; i < count; i++) { 6373 let top = label_date_items[i].getBoundingClientRect().top; 6374 if ((top - 100) < label_date_top && (top + 100) > label_date_top) { 6375 let label = $(label_date_items[label_date_history[0] > scroll && i > 0 ? i - 1 : i]).html(); 6376 if (label != label_date_history[1]) { 6377 SBChat.label_date.html(label); 6378 label_date_history[1] = label; 6379 } 6380 break; 6381 } 6382 } 6383 } 6384 } 6385 label_date_history[0] = scroll; 6386 }); 6387 6388 // Show the message menu 6389 $(chat).on('click', '.sb-menu-btn', function () { 6390 let menu = $(this).parent().find('.sb-menu'); 6391 let active = $(menu).sbActive(); 6392 SBF.deactivateAll(); 6393 if (!active) { 6394 $(menu).sbActive(true); 6395 SBF.deselectAll(); 6396 if (admin) SBAdmin.open_popup = menu; 6397 } 6398 }); 6399 6400 // Mobile 6401 if (mobile) { 6402 $(chat_editor).on('click', '.sb-textarea', function () { 6403 main.addClass('sb-header-hidden'); 6404 $(this).find('textarea').get(0).focus(); 6405 if (SBChat.isBottom()) { 6406 SBChat.scrollBottom(); 6407 setTimeout(() => { 6408 SBChat.scrollBottom(); 6409 }, 200); 6410 } 6411 }); 6412 6413 $(chat_editor).on('focusout', '.sb-textarea', function () { 6414 setTimeout(() => { 6415 main.removeClass('sb-header-hidden'); 6416 }, 300); 6417 }); 6418 6419 $(chat_editor).on('click', '.sb-submit', function () { 6420 chat_textarea.blur(); 6421 }); 6422 6423 window.addEventListener('popstate', function () { 6424 if (SBChat.chat_open) { 6425 SBChat.open(false); 6426 } 6427 }); 6428 } 6429 6430 // Hide the message menu 6431 $(chat).on('click', '.sb-menu li', function () { 6432 $(this).parent().sbActive(false); 6433 }); 6434 6435 // Send a message 6436 $(chat_editor).on('click', '.sb-submit', function () { 6437 SBChat.submit(); 6438 }); 6439 6440 // Clear a message 6441 $(chat_editor).on('click', '.sb-clear-text', function () { 6442 chat_textarea.val('').trigger('change').focus(); 6443 chat_editor.find('.sb-attachments').html(''); 6444 chat_editor.sbActive(false); 6445 }); 6446 6447 // Open the chat 6448 $('body').on('click', '.sb-chat-btn,.sb-responsive-close-btn, #sb-open-chat, .
6448sb-open-chat', function () { 6449 SBChat.open(!SBChat.chat_open); 6450 }); 6451 6452 // Show the dashboard 6453 $(main).on('click', '.sb-dashboard-btn', function () { 6454 SBChat.showDashboard(); 6455 if (chat_scroll_area.find(' > .sb-panel-articles > .sb-article').length) { 6456 SBChat.showArticles(); 6457 } 6458 storage('open-conversation', 0); 6459 force_action = false; 6460 }); 6461 6462 // Open a conversation from the dashboard 6463 $(main).on('click', '.sb-user-conversations li', function () { 6464 SBChat.openConversation($(this).attr('data-conversation-id')); 6465 }); 6466 6467 // Start a new conversation from the dashboard 6468 $(main).on('click', '.sb-btn-new-conversation, .sb-departments-list > div, .sb-agents-list > div', function () { 6469 let id = $(this).data('id'); 6470 let existing_conversation = false; 6471 if (!SBF.null(id)) { 6472 let is_departments = $(this).parent().hasClass('sb-departments-list'); 6473 if (is_departments) { 6474 SBChat.default_department = parseInt(id); 6475 } else { 6476 SBChat.default_agent = parseInt(id); 6477 } 6478 if ($(this).parent().data('force-one')) { 6479 let converstations = activeUser() ? activeUser().conversations : []; 6480 for (var i = 0; i < converstations.length; i++) { 6481 if ((is_departments && converstations[i].get('department') == id) || (!is_departments && converstations[i].get('agent_id') == id)) { 6482 existing_conversation = true; 6483 SBChat.openConversation(converstations[i].id); 6484 break; 6485 } 6486 } 6487 } 6488 } 6489 if (!existing_conversation) { 6490 force_action = 'new-conversation'; 6491 SBChat.clear(); 6492 SBChat.hideDashboard(); 6493 } 6494 }); 6495 6496 // Displays all conversations in the dashboard 6497 $(main).on('click', '.sb-btn-all-conversations', function () { 6498 main.find('.sb-dashboard-conversations').removeClass('sb-conversations-hidden'); 6499 }); 6500 6501 // Events uploader 6502 $(chat_editor).on('click', '.sb-btn-attachment', function () { 6503 if (!SBChat.is_busy) { 6504 chat_editor.find('.sb-upload-files').val('').click(); 6505 } 6506 }); 6507 6508 $(chat_editor).on('click', '.sb-attachments > div > i', function (e) { 6509 $(this).parent().remove(); 6510 if (!chat_textarea.val() && chat_editor.find('.sb-attachments > div').length == 0) { 6511 chat_editor.sbActive(false); 6512 } 6513 e.preventDefault(); 6514 return false; 6515 }); 6516 6517 $(chat_editor).on('change', '.sb-upload-files', function (data) { 6518 SBChat.busy(true); 6519 $(this).sbUploadFiles(function (response) { 6520 SBChat.uploadResponse(response); 6521 }); 6522 SBF.event('SBAttachments'); 6523 }); 6524 6525 $(chat_editor).on('dragover', function (e) { 6526 $(this).addClass('sb-drag'); 6527 clearTimeout(timeout); 6528 e.preventDefault(); 6529 e.stopPropagation(); 6530 }); 6531 6532 $(chat_editor).on('dragleave', function (e) { 6533 timeout = setTimeout(() => { 6534 $(this).removeClass('sb-drag'); 6535 }, 200); 6536 e.preventDefault(); 6537 e.stopPropagation(); 6538 }); 6539 6540 $(chat_editor).on('drop', function (e) { 6541 let files = e.originalEvent.dataTransfer.files; 6542 e.preventDefault(); 6543 e.stopPropagation(); 6544 if (files.length > 0) { 6545 for (var i = 0; i < files.length; ++i) { 6546 let form = new FormData(); 6547 form.append('file', files[i]); 6548 SBF.upload(form, function (response) { SBChat.uploadResponse(response) }); 6549 } 6550 } 6551 $(this).removeClass('sb-drag'); 6552 return false;
6553 }); 6554 6555 // Articles 6556 $(main).on('click', '.sb-articles > div, .sb-btn-all-articles:not([onclick])', function () { 6557 if ($(this).hasClass('sb-btn-all-articles')) { 6558 SBChat.showArticles(); 6559 } else { 6560 SBChat.showArticles($(this).attr('data-id'), $(this).attr('data-is-category')); 6561 if (!mobile) { 6562 SBChat.resize(); 6563 } 6564 } 6565 }); 6566 6567 $(main).on('click', '.sb-dashboard-articles .sb-input-btn .sb-submit-articles', function () { 6568 SBChat.searchArticles($(this).parent().find('input').val(), this, $(this).parent().next()); 6569 }); 6570 6571 $(global).on('click', '.sb-article [data-rating]', function () { 6572 SBChat.articleRatingOnClick(this); 6573 }); 6574 6575 $(chat).on('click', '.sb-rich-button a', function (e) { 6576 let link = $(this).attr('href'); 6577 if (link.indexOf('#') === 0) { 6578 if (link.indexOf('#article-') === 0) { 6579 SBChat.showArticles(link.replace('#article-', '')); 6580 e.preventDefault(); 6581 return false; 6582 } 6583 } 6584 }); 6585 6586 $('body').on('keydown', '.sb-articles-header input', function (e) { 6587 if (e.which == 13 && !mobile && !e.ctrlKey && !e.shiftKey) { 6588 $(this).next().click(); 6589 } 6590 }); 6591 6592 // Calls 6593 $(global).on('click', '#sb-call-answer', function () { 6594 if (loading(this)) return; 6595 SBF.call.answer(); 6596 }); 6597 6598 $(global).on('click', '#sb-call-decline', function () { 6599 if (loading(this)) return; 6600 SBF.call.decline(call_bar.hasClass('sb-call-active') || call_bar.hasClass('sb-call-ringing')); 6601 }); 6602 6603 $(global).on('click', '#sb-call-mute', function () { 6604 let is_muted = !$(this).hasClass('sb-btn-red'); 6605 SBF.call.mute(!is_muted); 6606 $(this).setClass('sb-btn-red', is_muted); 6607 }); 6608 6609 // Rating 6610 $(chat_overlay_panel).on('click', '[data-rating]', function () { 6611 let area = chat_overlay_panel.find('> div:last-child'); 6612 let rating = $(this).attr('data-rating'); 6613 let conversation = SBChat.conversation ? SBChat.conversation : activeUser().getLastConversation(); 6614 let agent = conversation.getLastUserMessage(false, true); 6615 loading(area); 6616 SBF.ajax({ 6617 function: 'set-rating', 6618 conversation_id: conversation.id, 6619 agent_id: agent ? agent.get('user_id') : false, 6620 user_id: activeUser().id, 6621 message: area.find('textarea').val(), 6622 rating: rating == 'positive' ? 1 : -1 6623 }, () => { 6624 if (chat_overlay_panel.attr('data-close-chat')) { 6625 SBChat.closeChat(); 6626 } 6627 SBChat.update(); 6628 chat_overlay_panel.sbActive(false); 6629 area.sbLoading(false); 6630 }); 6631 }); 6632 6633 // Lightbox 6634 $(global).on('click', '.sb-lightbox-media > i', function () { 6635 global.find('.sb-lightbox-media').sbActive(false); 6636 if (admin) { 6637 SBAdmin.open_popup = false; 6638 } 6639 return false; 6640 }); 6641 6642 $(main).on('click', '.sb-image', function () { 6643 SBF.lightbox($(this).html()); 6644 }); 6645 6646 $(main).on('click', '.sb-slider-images .sb-card-img', function () { 6647 SBF.lightbox(`<img loading="lazy" src="${$(this).attr('data-value')}" />`); 6648 }); 6649 6650 // Event: on conversation loaded 6651 $(document).on('SBConversationLoaded', function () { 6652 if (storage('queue')) { 6653 SBChat.queue(storage('queue')); 6654 } 6655 }); 6656 6657 // Events emoji 6658 $(chat_editor).on('click', '.sb-btn-emoji', function () { 6659 SBChat.showEmoji(this); 6660 }); 6661 6662 $(chat_emoji).on('click', '.sb-emoji-list li', function (e) { 6663 SBChat.insertEmoji($(this).html()); 6664 if (mobile) clearTimeout(timeout); 6665 }); 6666 6667 $(chat_emoji).find('.sb-emoji-list').on('touchend', function (e) { 6668 timeout = setTimeout(() => { 6669 SBChat.mouseWheelEmoji(e); 6670 }, 50); 6671 }); 6672 6673 $(chat_emoji).find('.sb-emoji-list').on('mousewheel DOMMouseScroll', function (e) { 6674 SBChat.mouseWheelEmoji(e); 6675 }); 6676 6677 $(chat_emoji).find('.sb-emoji-list').on('touchstart', function (e) { 6678 SBChat.emoji_options.touch = e.originalEvent.touches[0].clientY; 6679 }); 6680 6681 $(chat_emoji).on('click', '.sb-emoji-bar > div', function () { 6682 SBChat.clickEmojiBar(this); 6683 }); 6684 6685 $(chat_emoji).on('click', '.sb-select li', function () { 6686 SBChat.categoryEmoji($(this).data('value')); 6687 }); 6688 6689 $(chat_emoji).find('.sb-search-btn input').on('change keyup paste', function () { 6690 SBChat.searchEmoji($(this).val()); 6691 }); 6692 6693 // Textarea 6694 $(chat_textarea).on('keyup', function () { 6695 SBChat.textareaChange(this); 6696 }); 6697 6698 $(chat_textarea).on('focus input click', function () { 6699 chat_textarea.parent().parent().addClass('sb-focus'); 6700 }); 6701 6702 $(chat_textarea).on('focusout', function () { 6703 chat_textarea.parent().parent().removeClass('sb-focus'); 6704 }); 6705 6706 // Privacy message 6707 $(main).on('click', '.sb-privacy .sb-approve', function () { 6708 storage('privacy-approved', true); 6709 $(this).closest('.sb-privacy').remove(); 6710 main.removeClass('sb-init-form-active'); 6711 chat_header.find(' > div').css({ opacity: 1, top: 0 }); 6712 SBChat.initChat(); 6713 if (tickets) { 6714 SBTickets.showPanel(SBF.setting('tickets_disable_first') ? '' : 'new-ticket'); 6715 } else if (!SBChat.isInitDashboard()) { 6716 SBChat.hideDashboard(); 6717 } 6718 }); 6719 6720 $(main).on('click', '.sb-privacy .sb-decline', function () { 6721 let privacy = $(this).closest('.sb-privacy'); 6722 $(privacy).find('.sb-text').html($(privacy).attr('data-decline')); 6723 $(privacy).find('.sb-decline').remove(); 6724 SBChat.scrollBottom(true); 6725 }); 6726 6727 // Popup message 6728 $(main).on('click', '.sb-popup-message .sb-icon-close', function () { 6729 SBChat.popup(true); 6730 }); 6731 6732 // Rich messages and inputs 6733 $(main).on('click', '.sb-rich-message .sb-submit,.sb-rich-message:not(.sb-rich-registration) .sb-select ul li', function () { 6734 let message = $(this).closest('.sb-rich-message'); 6735 if (!message[0].hasAttribute('disabled')) { 6736 SBRichMessages.submit(message, message.attr('data-type'), this); 6737 }; 6738 }); 6739 6740 $(main).on('click', '.sb-rich-message .sb-input > span', function () { 6741 $(this).sbActive(true); 6742 $(this).siblings().focus(); 6743 }); 6744 6745 $(main).on('focus focusout click', '.sb-rich-message .sb-input input,.sb-rich-message .sb-input select', function (e) { 6746 switch (e.type) { 6747 case 'focusin': 6748 case 'focus': 6749 $(this).siblings().sbActive(true); 6750 break; 6751 case 'focusout': 6752 if ($(this).val()) { 6753 $(this).siblings().addClass('sb-filled sb-active'); 6754 } else { 6755 setTimeout(() => { 6756 if (!prevent_focusout) { 6757 $(this).siblings().sbActive(false); 6758 } 6759 }, 100); 6760 } 6761 break; 6762 case 'click': 6763 $(this).siblings().removeClass('sb-filled'); 6764 break; 6765 } 6766 }); 6767 6768 $(main).on('click', '.sb-input-select-input > div', function () { 6769 prevent_focusout = true; 6770 setTimeout(() => { 6771 prevent_focusout = false;
6772 }, 250); 6773 }); 6774 6775 $(main).on('click', '.sb-slider-arrow', function () { 6776 SBRichMessages.sliderChange($(this).closest('[id]').attr('id'), $(this).hasClass('sb-icon-arrow-right') ? 'right' : 'left'); 6777 }); 6778 6779 $(main).on('change', '.sb-rich-message [data-type="select"] select', function () { 6780 $(this).siblings().sbActive(true); 6781 }); 6782 6783 $(main).on('click', '[data-type="select-input"] > div', function () { 6784 $(this).prev().sbActive(true); 6785 $(this).next().addClass('sb-focus'); 6786 }); 6787 6788 $(main).on('focusout', '[data-type="select-input"] input,[data-type="select-input"] select', function () { 6789 let cnt = $(this).closest('.sb-input'); 6790 if (cnt.find('> input').val() + cnt.find('select').val() == '') { 6791 cnt.find('span').sbActive(false); 6792 } 6793 cnt.find('.sb-focus').removeClass('sb-focus'); 6794 }); 6795 6796 // Registration and Login 6797 $(main).on('click', '.sb-rich-message .sb-login-area', function () { 6798 let init = main.hasClass('sb-init-form-active'); 6799 $(this).closest('.sb-rich-message').replaceWith(SBRichMessages.generate({}, 'login', init ? 'sb-init-form' : '')); 6800 SBChat.scrollBottom(init); 6801 }); 6802 6803 $(main).on('click', '.sb-forgot-password', function () { 6804 let init = main.hasClass('sb-init-form-active'); 6805 $(this).closest('.sb-rich-login').replaceWith(SBRichMessages.generate({}, 'login-link', init ? 'sb-init-form' : '')); 6806 SBChat.scrollBottom(init); 6807 }); 6808 6809 $(main).on('click', '.sb-rich-login .sb-registration-area', function () { 6810 if (CHAT_SETTINGS.registration_link) { 6811 document.location = CHAT_SETTINGS.registration_link; 6812 } else { 6813 let init = main.hasClass('sb-init-form-active'); 6814 $(this).closest('.sb-rich-login').replaceWith(SBRichMessages.generate({}, 'registration', init ? 'sb-init-form' : '')); 6815 SBChat.scrollBottom(init); 6816 } 6817 }); 6818 6819 $(main).on('click', '.sb-rich-login .sb-submit-login', function () { 6820 SBF.loginForm(this, false, (response) => { 6821 let area = $(this).closest('.sb-rich-login'); 6822 activeUser(new SBUser(response[0])); 6823 if (area.hasClass('sb-init-form')) { 6824 force_action = 'open-conversation'; 6825 SBChat.initChat(); 6826 SBPusher.start(); 6827 if (!SBChat.isInitDashboard()) { 6828 SBChat.hideDashboard(); 6829 } 6830 $(document).on('SBPopulateConversations', function () { 6831 main.removeClass('sb-init-form-active'); 6832 area.remove(); 6833 $(document).off('SBPopulateConversations'); 6834 }); 6835 } else { 6836 area = area.closest('[data-id]'); 6837 let message = SBChat.conversation.getMessage(area.attr('data-id')); 6838 let text = `${sb_('Logged in as')} *${activeUser().name}*`; 6839 message.set('message', text); 6840 SBChat.updateMessage(message.id, text); 6841 area.replaceWith(message.getCode()); 6842 SBPusher.started = false; 6843 SBPusher.start(); 6844 } 6845 }); 6846 }); 6847 6848 $(main).on('click', '.sb-submit-login-link', function () { 6849 const area = $(this).closest('.sb-rich-login-link,.sb-panel'); 6850 const email = area.find('input').val(); 6851 if (email && email.includes('@') && email.includes('.') && !loading(this)) { 6852 SBF.ajax({ 6853 function: 'login-link', 6854 email: email, 6855 url: SBF.URL() 6856 }, () => { 6857 area.sbActive(true); 6858 }); 6859 } 6860 }); 6861 6862 // Social share buttons 6863 $(chat).on('click', '.sb-social-buttons div', function () { 6864 SBF.openWindow($(this).attr('data-link')); 6865 }); 6866 6867 // Archive chat 6868 $(main).on('click', '.sb-close-chat', function () { 6869 SBChat.closeChat(); 6870 }); 6871 6872 // Enlarge chat widget 6873 if (!mobile) { 6874 $(main).on('click', '.sb-enlarge-chat-widget', function () { 6875 SBChat.resize(!main.hasClass('sb-chat-large')); 6876 }); 6877 } 6878 6879 // WooCommerce 6880 $(chat).on('click', '.sb-rich-woocommerce_button a, [href="#"].sb-card-btn', function (e) { 6881 let settings = SBF.settingsStringToArray($(this).closest('.sb-rich-message').attr('data-settings')); 6882 let checkout = settings['link-type'] == 'checkout' || settings.checkout; 6883 let product_ids = $(this)[0].hasAttribute('data-ids') ? $(this).attr('data-ids').split(',') : [
6883settings.id.split('|')[$(this).parent().index()]]; 6884 if (product_ids.length) { 6885 if (loading(this)) return; 6886 SBApps.wordpress.ajax('button-purchase', { product_ids: product_ids, checkout: checkout, coupon: settings.coupon }, (response) => { 6887 if (checkout) { 6888 document.location = response; 6889 } else { 6890 $(this).addClass('sb-icon-check').sbLoading(false); 6891 } 6892 }); 6893 } 6894 e.preventDefault(); 6895 return false; 6896 }); 6897 6898 $(chat).on('click', '#sb-waiting-list .sb-submit', function () { 6899 if ($(this).index() == 0) { 6900 setTimeout(() => { SBApps.woocommerce.waitingList('submit') }, 1000); 6901 } 6902 }); 6903 6904 $(document).on('SBNewEmailAddress', function (e, response) { 6905 if (response.id == 'sb-waiting-list-email') { 6906 SBApps.woocommerce.waitingList('submit'); 6907 } 6908 }); 6909 6910 // Audio player 6911 $(chat).on('click', '.sb-player-btn', function () { 6912 SBF.audio.play(this); 6913 }); 6914 6915 $(chat).on('click', '.sb-player-speed', function () { 6916 SBF.audio.speed(this); 6917 }); 6918 6919 $(chat).on('click', '.sb-player-download', function () { 6920 SBF.audio.download(this); 6921 }); 6922 6923 // Audio recording 6924 $(chat_editor).on('click', '.sb-btn-audio-clip', function () { 6925 audio_mp3 = SBChat.conversation && SBChat.conversation.get('source') == 'wa'; 6926 if (audio_mp3 && typeof SBAudioRecorder === ND) { 6927 return $.getScript(SB_URL + '/vendor/lame.min.js', () => { this.click(); }, true); 6928 } 6929 navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => { 6930 if (audio_mp3) { 6931 SBAudioRecorder.init(stream); 6932 } else { 6933 if (typeof SBAudioRecorder != ND) { 6934 SBAudioRecorder.close(); 6935 } 6936 audio_recorder_chunks = []; 6937 audio_recorder = new MediaRecorder(stream); 6938 audio_recorder.addEventListener('dataavailable', e => { 6939 audio_recorder_chunks.push(e.data); 6940 }); 6941 } 6942 if (!audio_recorder_dom) { 6943 audio_recorder_dom = chat_editor.find('#sb-audio-clip'); 6944 audio_recorder_dom_time = audio_recorder_dom.find('.sb-audio-clip-time'); 6945 audio_recorder_dom.on('click', '.sb-btn-mic', function () { 6946 let is_pause = $(this).hasClass('sb-icon-pause'); 6947 if (is_pause) { 6948 if (audio_mp3) { 6949 SBAudioRecorder.pause(); 6950 } else { 6951 audio_recorder.stop(); 6952 } 6953 } else { 6954 let button = audio_recorder_dom.find('.sb-btn-clip-player'); 6955 if (button.hasClass('sb-icon-pause')) { 6956 button.click(); 6957 } 6958 if (audio_mp3) { 6959 SBAudioRecorder.resume(); 6960 } else { 6961 audio_recorder.start(); 6962 } 6963 } 6964 audio_recorder_time[2] = !is_pause; 6965 audio_recorder_dom.find('.sb-icon-play').sbActive(is_pause); 6966 $(this).removeClass('sb-icon-' + (is_pause ? 'pause' : 'mic')).addClass('sb-icon-' + (is_pause ? 'mic' : 'pause')); 6967 }); 6968 6969 audio_recorder_dom.on('click', '.sb-btn-clip-player', function () { 6970 let is_pause = $(this).hasClass('sb-icon-pause'); 6971 if (is_pause) { 6972 audio_recorder_time_player[3].pause(); 6973 audio_recorder_time_player[2] = false;
6974 } else { 6975 if (audio_recorder_time_player[3]) { 6976 audio_recorder_time_player[3].play(); 6977 audio_recorder_time_player[2] = true; 6978 } else { 6979 let audio = new Audio(URL.createObjectURL(audio_mp3 ? SBAudioRecorder.blob(false) : new Blob(audio_recorder_chunks, { type: 'audio/webm' }))); 6980 audio_recorder_dom_time.html('0:00'); 6981 audio.play(); 6982 audio.onended = () => { 6983 audio_recorder_time_player[0] = 0; 6984 audio_recorder_time_player[2] = false; 6985 audio_recorder_time_player[3] = false; 6986 audio_recorder_dom_time.html('0:00'); 6987 $(this).removeClass('sb-icon-pause').addClass('sb-icon-play'); 6988 } 6989 audio_recorder_time_player[0] = 0; 6990 audio_recorder_time_player[2] = true; 6991 audio_recorder_time_player[3] = audio; 6992 } 6993 } 6994 $(this).removeClass('sb-icon-' + (is_pause ? 'pause' : 'play')).addClass('sb-icon-' + (is_pause ? 'play' : 'pause')); 6995 }); 6996 6997 audio_recorder_dom.on('click', '.sb-icon-close', function () { 6998 audio_recorder_chunks = []; 6999 clearInterval(audio_recorder_time[1]); 7000 clearInterval(audio_recorder_time_player[1]); 7001 audio_recorder_dom.sbActive(false); 7002 chat_editor.sbActive(false).removeClass('sb-audio-message-active'); 7003 if (audio_mp3) { 7004 SBAudioRecorder.pause(); 7005 } else { 7006 audio_recorder.stop(); 7007 } 7008 audio_recorder_stream.getTracks()[0].stop(); 7009 }); 7010 } 7011 audio_recorder_dom_time.html('0:00'); 7012 audio_recorder_stream = stream; 7013 clearInterval(audio_recorder_time[1]); 7014 clearInterval(audio_recorder_time_player[1]); 7015 audio_recorder_time = [0, setInterval(() => { 7016 if (audio_recorder_time[2]) { 7017 audio_recorder_time[0]++; 7018 audio_recorder_dom_time.html(SBF.getMinutesSeconds(audio_recorder_time[0])); 7019 } 7020 }, 1000), true]; 7021 audio_recorder_time_player = [0, setInterval(() => { 7022 if (audio_recorder_time_player[2]) { 7023 audio_recorder_time_player[0]++; 7024 audio_recorder_dom_time.html(SBF.getMinutesSeconds(audio_recorder_time_player[0])); 7025 } 7026 }, 1000), false, false]; 7027 if (!audio_mp3) { 7028 audio_recorder.start(); 7029 } 7030 audio_recorder_dom.find('.sb-btn-clip-player').removeClass('sb-icon-pause').addClass('sb-icon-play').sbActive(false); 7031 audio_recorder_dom.find('.sb-icon-mic').removeClass('sb-icon-mic').addClass('sb-icon-pause'); 7032 audio_recorder_dom.sbActive(true); 7033 chat_textarea.val('').css('height', ''); 7034 chat_editor.sbActive(true).addClass('sb-audio-message-active'); 7035 }).catch(error => { 7036 alert(error); 7037 }); 7038 }); 7039 7040 // Overlay panel 7041 $(chat).on('click', '[data-action].sb-rich-btn', function (e) { 7042 SBRichMessages.iframe.load($(this).attr('data-action'), $(this).attr('data-extra'), $(this).html(), $(this).closest('[data-id]').attr('data-id')); 7043 e.preventDefault(); 7044 return false; 7045 }); 7046 7047 $(chat_overlay_panel).on('click', '> div:first-child i', function () { 7048 main.find('.sb-overlay-panel').sbActive(false); 7049 if (chat_overlay_panel.attr('data-close-chat')) { 7050 SBChat.closeChat(); 7051 } 7052 }); 7053 7054 // Phone 7055 $(main).on('change input', '#phone > input', function () { 7056 let value = $(this).val().trim(); 7057 if (!/^[0-9+]+$/.test(value)) { 7058 value = value.replace(/[^0-9+]/g, ''); 7059 $(this).val(value); 7060 } 7061 if (value.length > 1 && value.indexOf('+') === 0) { 7062 let prefix = false;
7063 if (value.substring(0, 2) == '+1') { 7064 prefix = value.substring(0, 2); 7065 } 7066 if (value.length > 3) { 7067 prefix = value.substring(0, 3); 7068 } 7069 if (prefix) { 7070 $(this).parent().find(`[data-value="${prefix}"]`).click(); 7071 $(this).parent().find(`.sb-select`).click(); 7072 $(this).val(value.replace(prefix, '')); 7073 } 7074 } 7075 }); 7076 7077 /* 7078 * ---------------------------------------------------------- 7079 * COMPONENTS 7080 * ---------------------------------------------------------- 7081 */ 7082 7083 // Search 7084 $(global).on('click', '.sb-search-btn > i', function () { 7085 let parent = $(this).parent(); 7086 let active = $(parent).sbActive(); 7087 if (active) { 7088 setTimeout(() => { $(parent).find('input').val('') }, 50); 7089 setTimeout(() => { $(parent).find('input').trigger('change') }, 550); 7090 }; 7091 $(parent).sbActive(!active); 7092 $(parent).find('input').get(0).focus(); 7093 global.find('.sb-select ul').sbActive(false); 7094 }); 7095 7096 // Select 7097 $(global).on('click', '.sb-select', function (e) { 7098 if (!e.target || !$(e.target).is('input')) { 7099 let ul = $(this).find('ul'); 7100 let active = ul.hasClass('sb-active'); 7101 $(global).find('.sb-select ul').sbActive(false); 7102 ul.setClass('sb-active', !active); 7103 $(this).find('.sb-select-search').setClass('sb-active', !active); 7104 if (admin) { 7105 SBAdmin.open_popup = active ? false : this; 7106 } 7107 } 7108 }); 7109 7110 $(global).on('click', '.sb-select li', function () { 7111 let select = $(this).closest('.sb-select'); 7112 let value = $(this).data('value'); 7113 let item = $(select).find(`[data-value="${value}"]`); 7114 select.find('li').sbActive(false); 7115 select.find('p').attr('data-value', value).html($(item).html()); 7116 item.sbActive(true); 7117 }); 7118 7119 $(global).on('input', '.sb-select-search input', function (e) { 7120 let search = $(this).val(); 7121 SBF.search(search, () => { 7122 let list = $(this).parent().parent().find('li'); 7123 list.setClass('sb-hide', search.length); 7124 if (search.length) { 7125 search = search.toLowerCase(); 7126 list.each(function () { 7127 if ($(this).attr('data-value').includes(search) || $(this).attr('data-country').includes(search)) { 7128 $(this).removeClass('sb-hide'); 7129 } 7130 }); 7131 } 7132 }); 7133 }); 7134 7135 // Image uploader 7136 $(global).on('click', '.sb-input-image .image', function () { 7137 upload_target = $(this).parent(); 7138 chat_editor.find('.sb-upload-files').click(); 7139 }); 7140 7141 $(global).on('click', '.sb-input-image .image > .sb-icon-close', function (e) { 7142 SBF.ajax({ function: 'delete-file', path: $(this).parent().attr('data-value') }); 7143 $(this).parent().removeAttr('data-value').css('background-image', ''); 7144 e.preventDefault(); 7145 return false; 7146 }); 7147 } 7148 7149}(jQuery));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.