/******************************************************************************* * Copyright (c) by Nguyen Thanh Phat. * Author: Nguyen Thanh Phat * Email: phat.nguyenthanh@meai.vn ******************************************************************************/ Chat3liUtils = { widgetId: "chat3li-widget-id-0307098", backgroundId: "background-radial", commands: ["COMMANDS.SHOW_WIDGET", "COMMANDS.HIDE_WIDGET"], ready: false, triggerHook: (type, data) => { if (!window.chat3liSettings || !window.chat3liSettings.hooks || !window.chat3liSettings.hooks[type]) { return; } try { window.chat3liSettings.hooks[type](data); } catch (e) { console.error(e); } }, _checkAndBindTOElement: () => { if (!window.chat3liSettings || !window.chat3liSettings.custom_launcher_selector) { return; } if (document && document.body) { checkingAndBind(); } else { window.onload = () => { checkingAndBind(); } } const checkingAndBind = () => { if (window.addEventListener) { document.body.addEventListener('click', clickHandler, true); } else { document.body.attachEvent('onclick', clickHandler, true); } } const clickHandler = (e) => { let target = e.target; while (target != null) { const isMatch = target.matches(window.chat3liSettings.custom_launcher_selector); if (isMatch) { chat3liApp.command("COMMANDS.SHOW_WIDGET", {}); return; } target = target.parentElement; } } }, extractWidgetDomain: () => { const scriptElem = document.getElementById('chat3li-chat-widget-bootstrap'); const url = scriptElem.getAttribute('src'); const regexp = /((https?:\/\/)[^/]+)/; const matchResult = regexp.exec(url); return matchResult && matchResult[1]; }, _keyStr: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=', delayLoad(fn, timeout) { if (document.readyState === 'complete') { setTimeout(fn, timeout); } else { window.onload = function () { setTimeout(fn, timeout); }; } }, lazyLoad: ['6825ebb93887558d30d26b5c85731307', 'c71db54c0e752bf9cd8952003693213d', '0517d09289e39f36d8567d78eee0fca1'], encode(input) { let output = ''; let chr1; let chr2; let chr3; let enc1; let enc2; let enc3; let enc4; let i = 0; input = this._utf8_encode(input); while (i < input.length) { chr1 = input.charCodeAt(i++); chr2 = input.charCodeAt(i++); chr3 = input.charCodeAt(i++); enc1 = chr1 >> 2; enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); enc4 = chr3 & 63; if (isNaN(chr2)) { enc3 = enc4 = 64; } else if (isNaN(chr3)) { enc4 = 64; } output = output + this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); } return output; }, _utf8_encode: (string) => { string = string.replace(/\r\n/g, '\n'); let utftext = ''; for (let n = 0; n < string.length; n++) { const c = string.charCodeAt(n); if (c < 128) { utftext += String.fromCharCode(c); } else if ((c > 127) && (c < 2048)) { utftext += String.fromCharCode((c >> 6) | 192); utftext += String.fromCharCode((c & 63) | 128); } else { utftext += String.fromCharCode((c >> 12) | 224); utftext += String.fromCharCode(((c >> 6) & 63) | 128); utftext += String.fromCharCode((c & 63) | 128); } } return utftext; } }; Chat3liApp = { ID_WIDGET: "chat3li-widget", state: {}, reset() { const currentWidget = document.getElementById(this.ID_WIDGET); if (currentWidget) { currentWidget.remove(); } }, init(config, context) { this.reset(); const initTime = context && context.initTime ? context.initTime : new Date().getTime(); if (this.initTime && initTime < this.initTime) { return; } this.initTime = initTime; let oldHash; const _hashHandler = () => { oldHash = window.location.href; const detect = function () { if (oldHash !== window.location.href) { return; } this.sendUrl(); oldHash = window.location.href; } this.state.invervalJob = setInterval(() => { detect; _checkFocus(); }, 100); } _hashHandler(); Chat3liUtils._checkAndBindTOElement(); config = config || {}; const domain = Chat3liUtils.extractWidgetDomain(); // const openerDomain = domain === 'http://3lichat' ? 'http://3lichat' : '*'; // if (!domain) { // return; // } let widgetContainers; window.addEventListener('message', _handleMsg, false); window.addEventListener('resize', () => { _calculateWidgetContainerMaxHeight(); }); _processWidget(config, {initTime}); function sendUrl() { _sendMessage({ title: window.document.title, url: window.location.href, referrer: window.document.referrer, search: window.location.search, type: 'change-url', }); } function _calculateWidgetContainerMaxHeight() { if (!widgetContainers) { return; } const clientRect = widgetContainers.getBoundingClientRect(); widgetContainers.style.maxHeight = `${clientRect.bottom}px`; _sendMessage({method: 'chat3li-iframe-max-height', maxHeight: clientRect.bottom}); } function _sendMessage(data) { const iframe = document.getElementById(Chat3liUtils.widgetId); if (!iframe) { return; } iframe.contentWindow.postMessage(data, '*'); } function _extractPath(object, path) { if (!path) { return object; } // normalization to array if (typeof path === 'string' || typeof path === 'number') { path = [path]; } let idx = 0; let result = object; while (path[idx] && result) { const pathPart = path[idx]; idx++; // 'attribute' type abbreviation if (typeof pathPart === 'string' || typeof pathPart === 'number') { result = result[pathPart]; continue; } if (pathPart.type === 'attribute') { result = result[pathPart.name]; continue; } if (pathPart.type === 'function') { if (!result[pathPart.name] || typeof result[pathPart.name] !== 'function') { result = null; continue; } let args = pathPart.args || []; args = args.map(_mapArg); result = result[pathPart.name].apply(result, args); } } return result; } function _mapArg(arg) { if (!arg.type) { return arg; } if (arg.type === 'object') { return arg.value; } if (arg.type == 'path') { const target = _getTargetElement(arg); return _extractPath(target, arg.path); } return null; } function _getTargetElement(eventConfig) { if (eventConfig.targetType === 'window') { return window; } if (eventConfig.targetType === 'document') { return document; } if (eventConfig.targetType === 'xpath') { return document.evaluate(eventConfig.targetPath, document).iterateNext(); } return null; } function _getExtractTarget(event, pathConfig) { if (pathConfig.targetType) { return _getTargetElement(pathConfig); } return event; } function _handleWidgetRequest(request) { const payload = request.payload; let target; let result; try { target = _getTargetElement(payload); result = _extractPath(target, payload.path); } catch (ex) { let errorMessage = ex && ex.message; errorMessage = typeof errorMessage === 'string' ? errorMessage : 'unknown_error'; _sendMessage({ method: 'chat3li-query-response', response: { requestId: request.requestId, status: 'failure', error: errorMessage, }, }); return; } _sendMessage({ method: 'chat3li-query-response', response: { result, status: 'success', requestId: request.requestId, }, }); } function _setupEventListener(listenerId, eventConfig) { let element; try { element = _getTargetElement(eventConfig); } catch (ex) { return; } if (!element) { return; } element.addEventListener(eventConfig.eventType, (e) => { // TODO: error handling const payload = {...eventConfig.baseData || {}}; if (eventConfig.paths) { eventConfig.paths.forEach((pathConfig) => { const extractTarget = _getExtractTarget(e, pathConfig); payload[pathConfig.key] = _extractPath(extractTarget, pathConfig.path); }); } _sendMessage({ method: 'chat3li-listen-event-triggered', payload, listenerId, }); }); } function _handleMsg(e) { if (!widgetContainers || !widgetContainers.classList) { return; } switch (e.data.method) { case 'chat3li-init-listen-event': _setupEventListener(e.data.listenerId, e.data.eventConfig); break; case 'chat3li-query': _handleWidgetRequest(e.data.request); break; case 'chat3li-boxchat-change-height': const newClass = `chat3li-widget-${e.data.value}`; const oldClass = newClass === 'chat3li-widget-show' ? 'chat3li-widget-hide' : 'chat3li-widget-show'; widgetContainers.classList.remove(oldClass); widgetContainers.classList.add(newClass); widgetContainers.style.height = null; widgetContainers.classList.remove('chat3li-widget-notif-new-message'); widgetContainers.classList.remove('chat3li-widget-resize'); if (e.data.value === 'show') { if (e.data.contextData && e.data.contextData.widgetSize) { widgetContainers.classList.add(`size-${e.data.contextData.widgetSize}`); } _calculateWidgetContainerMaxHeight(); if (window.innerWidth < 450) { document.body.style.overflow = 'hidden'; } Chat3liUtils.triggerHook('onShow'); updateBg(1); } else { if (e.data.contextData && e.data.contextData.widgetSize) { widgetContainers.classList.remove(`size-${e.data.contextData.widgetSize}`); } document.body.style.overflow = ''; Chat3liUtils.triggerHook('onHide'); updateBg(0); } break; case 'chat3li-boxchat-toggle-reading-size': if (e.data.className === 'oc-expand') { widgetContainers.classList.add('oc-widget-resize'); } else if (e.data.className === 'oc-shrink') { setTimeout(() => { widgetContainers.classList.remove('oc-widget-resize'); }, 300); } break; case 'chat3li-hidden-widget': widgetContainers.classList.add('hidden-widget'); updateBg(0); break; // TODO: Seem not used case 'chat3li-remove-hidden-boxchat': widgetContainers.classList.remove('hidden-widget'); break; case 'oc-chat-widget-notif-new-message': if (e.data.show) { widgetContainers.classList.add('oc-widget-notif-new-message'); widgetContainers.style.height = e.data.height; // height to notice height updateBg(1); } else { widgetContainers.classList.remove('oc-widget-notif-new-message'); widgetContainers.style.height = ''; // reset height updateBg(0); } _calculateWidgetContainerMaxHeight(); break; case 'close-notif': widgetContainers.classList.remove('oc-widget-notif-new-message'); widgetContainers.style.height = ''; updateBg(0); break; case 'oc-show-overlay-image': var iframe = document.getElementById('oc-modal-frame'); iframe.style.top = 0; iframe.style.display = 'block'; iframe.style.left = 0; iframe.style.width = '100%'; iframe.style.height = '100%'; iframe.style.position = 'fixed'; iframe.style['z-index'] = 2147483647; iframe.contentWindow.postMessage(e.data, domain); break; case 'oc-hide-overlay-image': var iframe = document.getElementById('oc-modal-frame'); iframe.style.display = 'none'; iframe.style.width = 0; iframe.style.height = 0; break; case 'oc-escape-pressed': var iframe = document.getElementById('oc-modal-frame'); iframe.contentWindow.postMessage(e.data, domain); break; case 'oc-widget-ready': Chat3liUtils.ready = true; break; case 'visitor:initialized': try { if (e.data && e.data.data) { Chat3liUtils.triggerHook('onVisitor', e.data.data); Chat3liUtils.visitor = e.data.data; console.log(Chat3liUtils.visitor); } } catch (error) { } break; case 'chat3li:feedback': initFeedback(e.data.data); break; default: break; } } function updateBg(show) { // eslint-disable-next-line no-undef let bg = document.getElementById(Chat3liUtils.backgroundId); const opacity = show === 1 ? 1 : 0; if (bg) { bg.style.opacity = opacity; return; } // eslint-disable-next-line no-undef bg = document.createElement('iframe'); bg.id = Chat3liUtils.backgroundId; bg.style.opacity = show; bg.classList.add('chat3li-radial-background'); if (!Chat3liUtils.wrapper) { return; } Chat3liUtils.wrapper.prepend(bg); } function _processWidget(config, context) { const srcUrl = document.getElementById('chat3li-chat-widget-bootstrap')?.getAttribute('src'); const appToken = getUrlParameter(srcUrl, 'token'); const widgetId = 'chat3li-widget'; const lang = getUrlParameter(srcUrl, 'lang') || 'vi'; const wrapper = document.createElement('div'); Chat3liUtils.wrapper = wrapper; wrapper.id = widgetId; wrapper.classList.add('chat3li-widget'); wrapper.classList.add('custom-css-override'); if (window.chat3liSetting && window.chat3liSetting.custom_launcher_selector) { wrapper.classList.add('chat3li-cl'); } widgetContainers = wrapper let iframe = document.getElementById(Chat3liUtils.widgetId); const data = JSON.stringify({ title: window.document.title, referrer: window.document.referrer, url: window.location.href, search: window.location.search, userAgent: window.navigator.userAgent }) let src = `${domain}/app/chat?widget-id=${widgetId}&token=${appToken}&chat3lidata=${Chat3liUtils.encode(data)}`; if (!iframe || iframe.length === 0) { _appendCSS(appToken); iframe = document.createElement('iframe'); iframe.id = Chat3liUtils.widgetId; iframe.style['border-width'] = '0px'; iframe.setAttribute('allowFullScreen', ''); iframe.style.width = '100%'; iframe.style.setProperty('height','100%','important'); iframe.style.setProperty('min-height','unset'); iframe.style.setProperty('max-height','unset'); iframe.classList.add('chat3li-iframe'); iframe.scrolling = 'no'; iframe.src = src; wrapper.appendChild(iframe); _addMainFrame(wrapper, appToken, context); } else { iframe.src = src; } } function _addMainFrame(frame, appToken, context) { const readyStateCheckInterval = setInterval(() => { if (!document || !document.body) { return; } clearInterval(readyStateCheckInterval); if (Chat3liUtils.lazyLoad.indexOf(appToken) >= 0) { Chat3liUtils.delayLoad(() => { _addFrames(frame, context); }, 2000); } else { _addFrames(frame, context); } }, 10); } function _addFrames(frame, context = {}) { if (context.initTime !== Chat3liApp.initTime) { return; } _addIframeToBody(frame); // _addModalFrameToBody(); } function _addIframeToBody(wrapper, cb) { document.body.appendChild(wrapper); if (cb) cb(); } function _addModalFrameToBody() { let iframe = document.getElementById('chat3li-modal-frame'); if (iframe) { return; } iframe = document.createElement('iframe'); iframe.id = 'chat3li-modal-frame'; iframe.src = `${domain}/modal.html`; iframe.frameBorder = 0; _addIframeToBody(iframe); } function _appendCSS(appToken) { _appendCSSUrl(`${domain}/api/v1/resources-api/widget-style.css?token=${appToken}&t=${new Date().getTime()}`); _appendCSSUrl(`${domain}/api/v1/resources-api/widget-style-override.css?token=${appToken}&t=${new Date().getTime()}`); } function _appendCSSUrl(url) { const link = document.createElement('link'); link.rel = 'stylesheet'; link.type = 'text/css'; link.href = url; document.head.appendChild(link); } function getUrlParameter(url, name) { name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]'); const regex = new RegExp(`[\\?&]${name}=([^&#]*)`); const results = regex.exec(url || window.location.search); return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' ')); } function _checkFocus() { if (this.isFocus === document.hasFocus()) { return; } this.isFocus = document.hasFocus(); _sendMessage({ value: this.isFocus, type: 'browser:focus-change', }); } }, } try { Chat3liApp.init(window.chat3liSetting, {initTime: new Date().getTime()}); } catch (ex) { console.log('Chat3li Error: ', ex); }