{"version":3,"file":"widget.js","sources":["../utils/utils.js","../utils/ajax.js","../../config.js","../utils/local-data.js","../utils/client-id.js","widget.js"],"sourcesContent":["const Utils = {\n isTouch: !!(('ontouchstart' in window) || (window.DocumentTouch && document instanceof window.DocumentTouch)),\n isIOS() {\n const ua = window.navigator.userAgent;\n const ipad = ua.match(/(iPad).*OS\\s([\\d_]+)/);\n const ipod = ua.match(/(iPod)(.*OS\\s([\\d_]+))?/);\n const iphone = !ipad && ua.match(/(iPhone\\sOS|iOS)\\s([\\d_]+)/);\n return ipad || iphone || ipod;\n },\n isIphone() {\n const ua = window.navigator.userAgent;\n const ipad = ua.match(/(iPad).*OS\\s([\\d_]+)/);\n const iphone = !ipad && ua.match(/(iPhone\\sOS|iOS)\\s([\\d_]+)/);\n return iphone;\n },\n guid() {\n /* eslint no-bitwise: [\"error\", { \"allow\": [\"~\", \">>\", \"&\", \"^\", \"|\"] }] */\n /* eslint no-mixed-operators: [\"allow\"] */\n if (window.crypto && window.Uint8Array) {\n return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>\n (c ^ window.crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)\n );\n }\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = Math.random() * 16 | 0;\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n },\n nextTick(callback, delay = 0) {\n return setTimeout(callback, delay);\n },\n nextFrame(callback) {\n if (window.requestAnimationFrame) return window.requestAnimationFrame(callback);\n else if (window.webkitRequestAnimationFrame) return window.webkitRequestAnimationFrame(callback);\n return window.setTimeout(callback, 1000 / 60);\n },\n now() {\n return Date.now();\n },\n extend(...args) {\n const to = Object(args[0]);\n for (let i = 1; i < args.length; i += 1) {\n const nextSource = args[i];\n if (nextSource !== undefined && nextSource !== null) {\n const keysArray = Object.keys(Object(nextSource));\n for (let nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex += 1) {\n const nextKey = keysArray[nextIndex];\n const desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);\n if (desc !== undefined && desc.enumerable) {\n if (typeof to[nextKey] === 'object' && typeof nextSource[nextKey] === 'object') {\n Utils.extend(to[nextKey], nextSource[nextKey]);\n } else {\n to[nextKey] = nextSource[nextKey];\n }\n }\n }\n }\n }\n return to;\n },\n each(obj, callback) {\n // Check it's iterable\n // TODO: Should probably raise a value error here\n if (typeof obj !== 'object') return;\n // Don't bother continuing without a callback\n if (!callback) return;\n if (Array.isArray(obj)) {\n // Array\n for (let i = 0; i < obj.length; i += 1) {\n // If callback returns false\n if (callback(i, obj[i]) === false) {\n // Break out of the loop\n return;\n }\n }\n } else {\n // Object\n for (let prop in obj) {\n // Check the propertie belongs to the object\n // not it's prototype\n if (obj.hasOwnProperty(prop)) {\n // If the callback returns false\n if (callback(prop, obj[prop]) === false) {\n // Break out of the loop;\n return;\n }\n }\n }\n }\n },\n serializeObject(obj, parents = []) {\n if (typeof obj === 'string') return obj;\n const resultArray = [];\n const separator = '&';\n let newParents;\n function varName(name) {\n if (parents.length > 0) {\n let parentParts = '';\n for (let j = 0; j < parents.length; j += 1) {\n if (j === 0) parentParts += parents[j];\n else parentParts += `[${encodeURIComponent(parents[j])}]`;\n }\n return `${parentParts}[${encodeURIComponent(name)}]`;\n }\n return encodeURIComponent(name);\n }\n function varValue(value) {\n return encodeURIComponent(value);\n }\n for (let prop in obj) {\n if (obj.hasOwnProperty(prop)) {\n let toPush;\n if (Array.isArray(obj[prop])) {\n toPush = [];\n for (let i = 0; i < obj[prop].length; i += 1) {\n if (!Array.isArray(obj[prop][i]) && typeof obj[prop][i] === 'object') {\n newParents = parents.slice();\n newParents.push(prop);\n newParents.push(String(i));\n toPush.push(Utils.serializeObject(obj[prop][i], newParents));\n } else {\n toPush.push(`${varName(prop)}[]=${varValue(obj[prop][i])}`);\n }\n }\n if (toPush.length > 0) resultArray.push(toPush.join(separator));\n } else if (obj[prop] === null || obj[prop] === '') {\n resultArray.push(`${varName(prop)}=`);\n } else if (typeof obj[prop] === 'object') {\n // Object, convert to named array\n newParents = parents.slice();\n newParents.push(prop);\n toPush = Utils.serializeObject(obj[prop], newParents);\n if (toPush !== '') resultArray.push(toPush);\n } else if (typeof obj[prop] !== 'undefined' && obj[prop] !== '') {\n // Should be string or plain value\n resultArray.push(`${varName(prop)}=${varValue(obj[prop])}`);\n } else if (obj[prop] === '') resultArray.push(varName(prop));\n }\n }\n return resultArray.join(separator);\n },\n\n};\n\nexport default Utils;\n","import Utils from './utils';\n\n// JSONP Requests\nlet jsonpRequests = 0;\n\n// Ajax\nfunction request(userOptions) {\n const defaults = {\n method: 'GET',\n data: false,\n async: true,\n cache: true,\n user: '',\n password: '',\n headers: {},\n xhrFields: {},\n statusCode: {},\n processData: true,\n dataType: 'text',\n contentType: 'application/x-www-form-urlencoded',\n timeout: 0,\n };\n const options = Utils.extend({}, defaults, userOptions);\n\n // For jQuery guys\n if (options.type) options.method = options.type;\n\n // Function to run XHR callbacks and events\n function fireAjaxCallback(callbackName, ...args) {\n // if (eventName) $(document).trigger(eventName, eventData);\n if (callbackName) {\n // Options callback\n if (options[callbackName]) options[callbackName](...args);\n }\n }\n\n // Default URL\n if (!options.url) {\n options.url = window.location.toString();\n }\n // Parameters Prefix\n let paramsPrefix = options.url.indexOf('?') >= 0 ? '&' : '?';\n\n // UC method\n const method = options.method.toUpperCase();\n\n // Data to modify GET URL\n if ((method === 'GET' || method === 'HEAD' || method === 'OPTIONS' || method === 'DELETE') && options.data) {\n let stringData;\n if (typeof options.data === 'string') {\n // Should be key=value string\n if (options.data.indexOf('?') >= 0) stringData = options.data.split('?')[1];\n else stringData = options.data;\n } else {\n // Should be key=value object\n stringData = Utils.serializeObject(options.data);\n }\n if (stringData.length) {\n options.url += paramsPrefix + stringData;\n if (paramsPrefix === '?') paramsPrefix = '&';\n }\n }\n // JSONP\n if (options.dataType === 'json' && options.url.indexOf('callback=') >= 0) {\n const callbackName = `f7jsonp_${Date.now() + ((jsonpRequests += 1))}`;\n let abortTimeout;\n const callbackSplit = options.url.split('callback=');\n let requestUrl = `${callbackSplit[0]}callback=${callbackName}`;\n if (callbackSplit[1].indexOf('&') >= 0) {\n const addVars = callbackSplit[1].split('&').filter(el => el.indexOf('=') > 0).join('&');\n if (addVars.length > 0) requestUrl += `&${addVars}`;\n }\n\n // Create script\n let script = document.createElement('script');\n script.type = 'text/javascript';\n script.onerror = function onerror() {\n clearTimeout(abortTimeout);\n fireAjaxCallback('error', null, 'scripterror');\n fireAjaxCallback('complete', null, 'scripterror');\n };\n script.src = requestUrl;\n\n // Handler\n window[callbackName] = function jsonpCallback(data) {\n clearTimeout(abortTimeout);\n fireAjaxCallback('success', data);\n script.parentNode.removeChild(script);\n script = null;\n delete window[callbackName];\n };\n document.querySelector('head').appendChild(script);\n\n if (options.timeout > 0) {\n abortTimeout = setTimeout(() => {\n script.parentNode.removeChild(script);\n script = null;\n fireAjaxCallback('error', null, 'timeout');\n }, options.timeout);\n }\n\n return undefined;\n }\n\n // Cache for GET/HEAD requests\n if (method === 'GET' || method === 'HEAD' || method === 'OPTIONS' || method === 'DELETE') {\n if (options.cache === false) {\n options.url += `${paramsPrefix}_nocache${Date.now()}`;\n }\n }\n\n // Create XHR\n const xhr = new window.XMLHttpRequest();\n\n // Save Request URL\n xhr.requestUrl = options.url;\n xhr.requestParameters = options;\n\n // Open XHR\n xhr.open(method, options.url, options.async, options.user, options.password);\n\n // Create POST Data\n let postData = null;\n\n if ((method === 'POST' || method === 'PUT' || method === 'PATCH') && options.data) {\n if (options.processData) {\n const postDataInstances = [ArrayBuffer, Blob, Document, FormData];\n // Post Data\n if (postDataInstances.indexOf(options.data.constructor) >= 0) {\n postData = options.data;\n } else {\n // POST Headers\n let boundary = `---------------------------${Date.now().toString(16)}`;\n\n if (options.contentType === 'multipart\\/form-data') {\n xhr.setRequestHeader('Content-Type', `multipart\\/form-data; boundary=${boundary}`);\n } else {\n xhr.setRequestHeader('Content-Type', options.contentType);\n }\n postData = '';\n let data = Utils.serializeObject(options.data);\n if (options.contentType === 'multipart\\/form-data') {\n boundary = `---------------------------${Date.now().toString(16)}`;\n data = data.split('&');\n const newData = [];\n for (let i = 0; i < data.length; i += 1) {\n newData.push('Content-Disposition: form-data; name=\"' + data[i].split('=')[0] + '\"\\r\\n\\r\\n' + data[i].split('=')[1] + '\\r\\n');\n }\n postData = `--${boundary}\\r\\n${newData.join(`--${boundary}\\r\\n`)}--${boundary}--\\r\\n`;\n } else {\n postData = data;\n }\n }\n } else {\n postData = options.data;\n }\n }\n\n // Additional headers\n if (options.headers) {\n Utils.each(options.headers, (headerName, headerCallback) => {\n xhr.setRequestHeader(headerName, headerCallback);\n });\n }\n\n // Check for crossDomain\n if (typeof options.crossDomain === 'undefined') {\n options.crossDomain = /^([\\w-]+:)?\\/\\/([^\\/]+)/.test(options.url) && RegExp.$2 !== window.location.host;\n }\n\n if (!options.crossDomain) {\n xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');\n }\n\n if (options.xhrFields) {\n Utils.each(options.xhrFields, (fieldName, fieldValue) => {\n xhr[fieldName] = fieldValue;\n });\n }\n\n let xhrTimeout;\n // Handle XHR\n xhr.onload = function onload(e) {\n if (xhrTimeout) clearTimeout(xhrTimeout);\n if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 0) {\n let responseData;\n if (options.dataType === 'json') {\n try {\n responseData = JSON.parse(xhr.responseText);\n fireAjaxCallback('success', responseData, xhr.status, xhr);\n } catch (err) {\n fireAjaxCallback('error', xhr, 'parseerror');\n }\n } else {\n responseData = xhr.responseType === 'text' || xhr.responseType === '' ? xhr.responseText : xhr.response;\n fireAjaxCallback('success', responseData, xhr.status, xhr);\n }\n } else {\n fireAjaxCallback('error', xhr, xhr.status);\n }\n if (options.statusCode) {\n if (options.statusCode[xhr.status]) options.statusCode[xhr.status](xhr);\n }\n fireAjaxCallback('complete', xhr, xhr.status);\n };\n\n xhr.onerror = function onerror() {\n if (xhrTimeout) clearTimeout(xhrTimeout);\n fireAjaxCallback('error', xhr, xhr.status);\n fireAjaxCallback('complete', xhr, 'error');\n };\n\n // Ajax start callback\n fireAjaxCallback('start', xhr);\n fireAjaxCallback('beforeSend', xhr);\n\n // Timeout\n if (options.timeout > 0) {\n xhr.onabort = function onabort() {\n if (xhrTimeout) clearTimeout(xhrTimeout);\n };\n xhrTimeout = setTimeout(() => {\n xhr.abort();\n fireAjaxCallback('error', xhr, 'timeout');\n fireAjaxCallback('complete', xhr, 'timeout');\n }, options.timeout);\n }\n\n // Send XHR\n xhr.send(postData);\n\n // Return XHR object\n return xhr;\n}\n\nfunction ajaxShortcut(method, ...args) {\n let url;\n let data;\n let success;\n let error;\n let dataType;\n if (typeof args[1] === 'function') {\n [url, success, error, dataType] = args;\n } else {\n [url, data, success, error, dataType] = args;\n }\n [success, error].forEach((callback) => {\n if (typeof callback === 'string') {\n dataType = callback;\n if (callback === success) success = undefined;\n else error = undefined;\n }\n });\n dataType = dataType || (method === 'getJSON' ? 'json' : undefined);\n return request({\n url,\n method: method === 'post' ? 'POST' : 'GET',\n data,\n success,\n error,\n dataType,\n });\n}\n\nfunction get(...args) {\n args.unshift('get');\n return ajaxShortcut.apply(this, args);\n}\nfunction post(...args) {\n args.unshift('post');\n return ajaxShortcut.apply(this, args);\n}\nfunction getJSON(...args) {\n args.unshift('getJSON');\n return ajaxShortcut.apply(this, args);\n}\n\nexport default { request, get, post, getJSON };\n","const config = {\n};\n\nlet extendConfig = {};\nif (process.env.NODE_ENV === 'production') {\n /* ================================\n PRODUCTION\n ================================ */\n extendConfig = {\n api: 'https://app.letsbrief.com/api',\n socket: 'https://app.letsbrief.com',\n appHost: 'https://widget.letsbrief.com',\n appScript: 'https://widget.letsbrief.com/app/app.min.js',\n fontsPath: 'https://widget.letsbrief.com/fonts',\n filesPath: 'https://brief.imgix.net',\n };\n} else {\n /* ================================\n DEVELOPMENT\n ================================ */\n extendConfig = {\n api: 'http://localhost:7000/api',\n socket: 'http://localhost:7000',\n appHost: 'http://localhost:3000/',\n appScript: 'http://localhost:3000/app/app.js',\n fontsPath: 'http://localhost:3000/fonts',\n filesPath: 'https://brief.imgix.net',\n };\n}\nObject.keys(extendConfig).forEach((key) => {\n config[key] = extendConfig[key];\n});\n\nexport default config;\n","export default {\n set(key, value) {\n let data;\n try {\n data = JSON.parse(window.localStorage.briefWidget || '{}');\n } catch (e) {\n // No ls support or no data stored\n }\n if (data) {\n data[key] = value;\n window.localStorage.briefWidget = JSON.stringify(data);\n }\n return data || {};\n },\n get(key) {\n let data;\n try {\n data = JSON.parse(window.localStorage.briefWidget || '{}');\n } catch (e) {\n // No ls support or no data stored\n }\n if (!data) return undefined;\n return data[key];\n },\n remove(key) {\n let data;\n try {\n data = JSON.parse(window.localStorage.briefWidget || '{}');\n } catch (e) {\n // No ls support or no data stored\n }\n if (data) {\n if (data[key]) {\n data[key] = undefined;\n delete data[key];\n }\n window.localStorage.briefWidget = JSON.stringify(data);\n }\n },\n clear() {\n try {\n window.localStorage.removeItem('briefWidget');\n } catch (e) {\n // No ls support or no data stored\n }\n },\n};\n","import Utils from '../utils/utils';\nimport LocalData from '../utils/local-data';\n\n// User ID\nlet clientId = LocalData.get('clientId');\n\nif (!clientId) {\n clientId = Utils.guid();\n LocalData.set('clientId', clientId);\n}\n\nconst cid = clientId;\n\nexport default cid;\n","import Utils from '../utils/utils';\nimport ajax from '../utils/ajax';\nimport WidgetStyles from './widget.css';\nimport AppStyles from './app.css';\nimport AppContent from './app-content.html';\nimport config from '../../config';\nimport clientId from '../utils/client-id';\nimport LocalData from '../utils/local-data';\n\nlet cid = clientId;\n\nconst eventListeners = {};\n \nconst Widget = {\n emit(...args) {\n const events = args[0].split(' ');\n const data = args.slice(1, args.length);\n events.forEach((eventName) => {\n if (!eventListeners[eventName]) return;\n eventListeners[eventName].forEach((eventCallback) => {\n eventCallback(...data);\n });\n });\n },\n on(events, handler) {\n if (typeof handler !== 'function') return;\n events.split(' ').forEach((event) => {\n if (!eventListeners[event]) eventListeners[event] = [];\n eventListeners[event].push(handler);\n });\n },\n themeColor: '#825ae3',\n fixViewport() {\n let viewport = document.querySelector('meta[name=\"viewport\"]');\n if (!viewport) {\n viewport = document.createElement('meta');\n viewport.setAttribute('name', 'viewport');\n viewport.setAttribute('content', 'initial-scale=1');\n document.querySelector('head').appendChild(viewport);\n return;\n }\n },\n addStyles() {\n const styleEl = document.createElement('style');\n styleEl.innerHTML = WidgetStyles.replace(/\\$themeColor/g, Widget.themeColor).replace(/\\$fontsPath/g, config.fontsPath);\n document.head.appendChild(styleEl);\n },\n addLayout() {\n // iFrame\n Widget.iframe = document.createElement('iframe');\n Widget.iframe.scrolling = 'no';\n Widget.iframe.allowfullscreen = true;\n\n // iFrame Wrap\n Widget.frame = document.createElement('div');\n Widget.frame.id = 'brief-widget-frame';\n Widget.frame.appendChild(Widget.iframe);\n\n // Container\n Widget.container = document.createElement('div');\n Widget.container.id = 'brief-widget-container';\n Widget.container.appendChild(Widget.frame);\n Widget.container.className += `brief-widget-container-${Widget.settings.position}`;\n\n // Notifications\n Widget.notificationsIframe = document.createElement('iframe');\n Widget.notificationsIframe.scrolling = 'no';\n\n Widget.notificationsFrame = document.createElement('div');\n Widget.notificationsFrame.id = 'brief-widget-notifications';\n Widget.notificationsFrame.appendChild(Widget.notificationsIframe);\n Widget.notificationsFrame.className += `brief-widget-notifications-${Widget.settings.position}`;\n\n // Opener\n if (window.BriefElementId) {\n Widget.launcher = document.getElementById(window.BriefElementId || 'brief-chat-button');\n const unreadElement = document.getElementById(window.window.BriefUnreadId || 'brief-unread-count');\n if (unreadElement) {\n if (Widget.settings.unread > 0) {\n unreadElement.innerHTML = Widget.settings.unread;\n unreadElement.style.visibility = 'visible';\n } else {\n unreadElement.style.visibility = 'hidden';\n }\n }\n Widget.frame.className += 'no-button';\n } else {\n Widget.launcher = document.createElement('div');\n Widget.launcher.id = 'brief-widget-launcher';\n Widget.launcher.className += `brief-widget-launcher-${Widget.settings.position}`;\n if (Widget.settings.unread > 0) {\n Widget.launcher.innerHTML = `