1function isMobile() { 2 return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( 3 navigator.userAgent 4 ); 5 } 6 7 const defaultOptions = { 8 withWelcomeScreen: false, 9 welcomeMessage: "GetStarted", //First message sent to Studio when the user start the chat 10 openAfterTimeout: isMobile(true) ? undefined : 3000, //Value in miliseconds. Remove this line if you don't want to a start chat when user clicks in the chat bubble 11 withLocation: false 12 }; 13 14 function bootstrapLoader(webchat, options = defaultOptions) { 15 const { welcomeMessage, withWelcomeScreen, openAfterTimeout, withLocation } = { 16 ...defaultOptions, 17 ...options 18 }; 19 20 if (!webchat.isLiveChat) { 21 return; 22 } 23 24 const cssFile = document.createElement("link"); 25 cssFile.rel = "stylesheet"; 26 cssFile.href = "/-/media/scripts/loader.css"; 27 28 const shadowRoot = webchat.injector.shadowRoot; 29 shadowRoot.appendChild(cssFile); 30 31 // Create panel and loader 32 const panel = document.createElement("div"); 33 panel.className = "botonic-loader-panel"; 34 35 var template = document.createElement("template"); 36 template.innerHTML = 37 '<div class="lds-ellipsis"><div></div><div></div><div></div><div></div></div>'; 38 const loader = template.content; 39 panel.appendChild(loader); 40 panel.style.display = "none"; 41 42 // MonkeyPatch the onOpen event 43 const onOpen = webchat.onOpenWebchat; 44 45 function hideLoader() { 46 panel.style.display = "none"; 47 } 48 49 function showLoader() { 50 panel.style.display = "block"; 51 52 setTimeout(hideLoader, 6000); 53 } 54 55 let initialized = false; 56 57 const startProcess = () => { 58 showLoader(); 59 60 let firstMessage = welcomeMessage; 61 62 if(withLocation){ 63 const l = withLocation 64 ? localStorage.getItem("currentLocation") 65 : undefined; 66 67 firstMessage = withLocation 68 ? l 69 ? "Hello from " + l 70 : "Helloãâ¦Â¤" 71 : firstMessage; 72 73 } 74 75 setTimeout(() => { 76 webchat.selfHostedApp.addUserMessage({ 77 data: firstMessage, 78 type: "postback" 79 }); 80 }, 100); 81 }; 82 83 const hasClass = (path, cls) => { 84 return path?.some((p) => { 85 try { 86 return p?.className?.startsWith(cls); 87 } catch (e) { 88 return false; 89 } 90 }); 91 }; 92 93 const startButtonClass = "customStartChatButton_"; 94 95 const getForm = () => webchat.injector.shadowRoot.querySelector("form"); 96 97 const onSubmit = (e) => { 98 const form = getForm(); 99 100 if (form) { 101 form.removeEventListener("submit", onSubmit); 102 } 103 104 if (!isFormValid()) { 105 e.preventDefault(); 106 } else { 107 startProcess(); 108 } 109 }; 110 111 const isFormValid = () => { 112 return ( 113 webchat.injector.shadowRoot.querySelectorAll('[class*="isError"]') 114 .length === 0 115 ); 116 }; 117 118 // Add event listeners 119 webchat.injector.addEventListener("click", (e) => { 120 if (hasClass(e.composedPath(), "submitButton_")) { 121 if (withWelcomeScreen) { 122 startProcess(); 123 } 124 return; 125 } 126 127 // When the user clicks on the "Start new chat" 128 if (hasClass(e.composedPath(), startButtonClass) && e.isTrusted) { 129 if (!withWelcomeScreen) { 130 setTimeout(startProcess); 131 } 132 133 // const ref = webchat.selfHostedApp.webchatRef.current; 134 // ref.updateWebchatSettings({ enableUserInput: false }); 135 } 136 }); 137 138 window.showLoader = showLoader; 139 140 const defaultConfig = { 141 // Undefined on purpose 142 livechatConversationEnded: undefined 143 }; 144 145 const getWebchatConfig = () => { 146 try { 147 const item = localStorage.getItem("webchatConfig"); 148 149 if (item) { 150 return JSON.parse(item); 151 } 152 153 return defaultConfig; 154 } catch (e) { 155 console.error(e); 156 return defaultConfig; 157 } 158 }; 159 160 const querySelector = (selector) => 161 webchat.injector.shadowRoot.querySelector(selector); 162 163 const getByClass = (className) => querySelector("." + className); 164 165 const getStartButton = () => { 166 return querySelector(`[class^="${startButtonClass}"]`); 167 }; 168 169 const getLocation = () => { 170 if (!withLocation) { 171 return undefined; 172 } 173 174 const currentLocation = localStorage.getItem("currentLocation"); 175 176 if (currentLocation) { 177 return Promise.resolve(currentLocation); 178 } 179 180 return fetch("https://ipapi.co/json/") 181 .then((apiResult) => apiResult.json()) 182 .then((jsonData) => jsonData.city + " - " + jsonData.country_name) 183 .then((l) => { 184 localStorage.setItem("currentLocation", l); 185 return l; 186 }) 187 .catch(() => { 188 return undefined; 189 }); 190 }; 191 192 const restartSession = async () => { 193 // Wait until the start button is visible 194 await new Promise((resolve) => { 195 const interval = setInterval(() => { 196 const $startButton = getStartButton(); 197 const $endChat = getByClass("end-chat-message"); 198 199 if ($startButton) { 200 $startButton.style.display = "none"; 201 } 202 203 if (!$startButton || !$endChat) { 204 return; 205 } 206 207 // Remove the interval 208 clearInterval(interval); 209 210 // Click on the start button 211 $startButton.click(); 212 resolve(); 213 }, 1); 214 }); 215 216 // Wait until the Webchat SDK updates the config 217 await new Promise((resolve) => { 218 const interval = setInterval(() => { 219 const { livechatConversationEnded } = getWebchatConfig(); 220 221 if (!livechatConversationEnded) { 222 clearInterval(interval); 223 resolve(); 224 } 225 }, 1); 226 }); 227 }; 228 229 const onOpenWebchat = async () => { 230 const botonic = shadowRoot.getElementById("botonic-webchat"); 231 232 if (!botonic) { 233 console.warn("Couldn't initialize the loader for botonic and live chat"); 234 return; 235 } 236 237 // Check if the loader is correctly loaded 238 if (panel.parentElement !== botonic) { 239 // Append loader 240 botonic.appendChild(panel); 241 } 242 243 if (initialized) { 244 return; 245 } 246 247 initialized = true; 248 await getLocation(); 249 250 // Let's see if the conversation ended from config 251 const { livechatConversationEnded } = getWebchatConfig(); 252 253 if (livechatConversationEnded) { 254 showLoader(); 255 //await restartSession(); 256 257 if (!withWelcomeScreen) { 258 startProcess(); 259 } else { 260 hideLoader(); 261 } 262 263 return; 264 } 265 266 const lastReadMessageIndex = localStorage.getItem("lastReadMessageIndex"); 267 268 if (!lastReadMessageIndex || lastReadMessageIndex === "null") { 269 if (!withWelcomeScreen) { 270 // Show the loader and send the payload 271 // When opening the widget on the first time 272 startProcess(); 273 } 274 } 275 }; 276 277 webchat.onOpenWebchat = function () { 278 onOpen.call(webchat); 279 onOpenWebchat(); 280 }; 281 282 // Open the web widget after a specified timeout 283 if (openAfterTimeout) { 284 const $button = webchat.injector.shadowRoot.querySelector( 285 "[data-qa='trigger-button']" 286 ); 287 288 const { wasChatOpened } = getWebchatConfig(); 289 290 if (wasChatOpened === undefined && $button) { 291 setTimeout(() => { 292 $button.click(); 293 }, openAfterTimeout); 294 } 295 } 296 } 297 298 window.virtualAgentBootstrapLoader = bootstrapLoader; 299 300// JavaScript Document
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.