1document.addEventListener('DOMContentLoaded', function () { 2 $('.chat__loader').addClass('active') 3 let copilotInitialised = false; 4 let webchatDiv = document.getElementById('webchat'); 5 let chatId = null 6 let savedChatId = null 7 function toggleChatView(close= false){ 8 if (!copilotInitialised) { 9 initCopilot(); 10 } 11 copilotInitialised = true; 12 const chatContainer = webchatDiv.parentElement; 13 const chatIcon = document.getElementById('chat-icon'); 14 const isVisible = chatContainer.style.display === 'flex'; 15 if (isVisible) { 16 if(!close){ 17 $('.chat__modal').addClass('active') 18 $('.chat__modal-tab').removeClass('active') 19 $('.chat__modal-choose').addClass('active') 20 } else { 21 chatContainer.style.display = 'none' 22 chatIcon.classList.remove('cancel-icon'); 23 } 24 } else { 25 chatContainer.style.display = 'flex' 26 chatIcon.classList.add('cancel-icon'); 27 } 28 } 29 document.getElementById('chat-icon').addEventListener('click', function (e) { 30 e.stopPropagation(); 31 toggleChatView() 32 }); 33 34 function loadWebChatSDK() { 35 return new Promise((resolve, reject) => { 36 if (typeof WebChat !== 'undefined') { 37 return resolve(); 38 } 39 const script = document.createElement('script'); 40 script.src = 'https://cdn.botframework.com/botframework-webchat/latest/webchat.js'; 41 script.onload = () => resolve(); 42 script.onerror = () => reject(new Error('Failed to load WebChat SDK')); 43 document.head.appendChild(script); 44 }); 45 } 46 47 function removeCopilotDivs(webchatDiv) { 48 localStorage.removeItem('pva_conversationId'); 49 localStorage.removeItem('pva_token'); 50 if (webchatDiv) { 51 webchatDiv.innerHTML = ''; 52 } 53 const chatIcon = document.getElementById('chat-icon'); 54 if (chatIcon) chatIcon.remove(); 55 } 56 57 async function initCopilot(reinit = true) { 58 $('.chat__loader').addClass('active') 59 60 await loadWebChatSDK(); 61 62 if (!webchatDiv) { 63 removeCopilotDivs(); 64 return; 65 } 66 try { 67 const styleOptions = { 68 accent: '#034EA2', 69 botAvatarBackgroundColor: '#000000', 70 // botAvatarImage: 'https://i.imgur.com/v0pQRJe.jpeg', 71 // botAvatarInitials: ' ', 72 userAvatarBackgroundColor: '#555555', 73 // userAvatarImage: 'https://i.imgur.com/Vd2rwWx.jpeg', 74 // userAvatarInitials: ' ', 75 backgroundColor: '#f4f6f9',
76 bubbleBackground: '#f7f2ee', 77 bubbleTextColor: '#333333', 78 bubbleFromUserBorderColor: '#034EA2', 79 bubbleFromUserBackground: '#dceeff', 80 bubbleFromUserTextColor: '#333333', 81 sendBoxBackground: '#F4F4F4', 82 sendBoxTextColor: '#333333', 83 sendBoxButtonColor: '#000000', 84 avatarBorderRadius: '50%', 85 sendBoxAutoExpand: true, 86 sendBoxMaxHeight: 250, 87 avatarSize: 67, 88 showAvatarInGroup: true, 89 bubbleBorderRadius: '8px', 90 bubbleFromUserBorderRadius: '8px', 91 bubbleNubSize: '10px', 92 bubbleFromUserNubSize: '10px', 93 sendBoxButtonColorOnFocus: '#034EA2', 94 sendBoxButtonColorOnHover: '#034EA2', 95 connectivityIconPadding: '10px', 96 connectivityMarginTopBottom: '10px', 97 notificationText: '#4A4A4A', 98 transcriptOverlayBackground: 'rgba(255, 255, 255, 0.8)', 99 transcriptOverlayPadding: '20px', 100 transcriptVerticalSpacer: '10px', 101 hideUploadButton: true, 102 suggestedActionLayout: 'stacked', // ð this makes them vertical 103 suggestedActionBorderRadius: 8, 104 suggestedActionBackgroundColor: '#E2E2E2', 105 suggestedActionTextColor: '#333333', 106 suggestedActionHeight: 40, 107 suggestedActionPadding: 10 108 }; 109 110 const locale = document.documentElement.lang || 'lt'; 111 112 let savedToken = localStorage.getItem('pva_token'); 113 114 const [directLineURL, token] = await Promise.all([ 115 fetch('/general/copilot_directline_url') 116 .then(response => { 117 if (!response.ok) { 118 throw new Error('Failed to retrieve regional channel settings.'); 119 } 120 121 return response.json(); 122 }) 123 .then(({channelUrlsById: {directline}}) => directline) 124 .catch(error => { 125 return null; 126 }), 127 fetch('/general/copilot_token') 128 .then(response => { 129 if (!response.ok) { 130 throw new Error('Failed to retrieve Direct Line token.'); 131 } 132 return response.json(); 133 }) 134 .then(({token}) => savedToken ? savedToken : token) 135 // .then(({ token }) => token) 136 .catch(error => { 137 console.error('Error fetching Direct Line token:', error); 138 return null; 139 }) 140 ]); 141 142 if (!directLineURL || !token) { 143 removeCopilotDivs(webchatDiv); 144 return; 145 } 146 147 if (token && (!savedToken || savedToken !== token)) { 148 localStorage.setItem('pva_token', token); 149 } 150 151 console.log(token, directLineURL); 152 153 // NOTE: RETRY ONLY ON TOKENS FROM SESSION! 154 const directLine = WebChat.createDirectLine({ 155 domain: new URL('v3/directline', directLineURL), 156 token 157 }); 158 159 const subscription = directLine.connectionStatus$.subscribe({ 160 next(value) { 161 if (value === 2) { 162 $('.chat__loader').removeClass('active') 163 chatId = directLine.conversationId 164 directLine.postActivity({ 165 localTimezone: Intl.DateTimeFormat().resolvedOptions().timeZone, 166 locale, 167 name: 'startConversation', 168 type: 'event' 169 }).subscribe(); 170 directLine.activity$.subscribe( 171 activity => { 172 console.log('Received activity:', activity); 173 if(activity.type !== 'typing'){ 174 fetch('/cpanalytics/store', { 175 method: 'POST', 176 headers: { 177 'Content-Type': 'application/json' 178 }, 179 body: JSON.stringify({ 180 type: 'event', 181 data: activity, 182 conversation_id: chatId, 183 }) 184 }) 185 } 186 // TODO Save start conversation / end conversation events 187 // save: start/end timestamp 188 189 // Process the activity here 190 if (activity.type === 'message') { 191 console.log('Received message:', activity.text); 192 // Handle message activities (e.g., display in UI) 193 } 194 // You can also filter by 'from.id' to distinguish bot messages from echoed user messages 195 // if (activity.type === 'message' && activity.from.id === 'yourBotHandle') { 196 // console.log('Received bot message:', activity.text); 197 // } 198 }, 199 error => { 200 console.error('Error receiving activity:', error); 201 } 202 ); 203 subscription.unsubscribe(); 204 } else if (value === 4) { 205 console.warn('DirectLine connection dropped (value = 4)'); 206 207 if (reinit) { 208 localStorage.removeItem('pva_token'); 209 210 try { 211 directLine && directLine.end && directLine.end(); 212 } catch (e) { 213 console.error('Error ending previous directLine:', e); 214 } 215 216 if (webchatDiv) { 217 webchatDiv.innerHTML = ''; 218 } 219 220 setTimeout(() => { 221 initCopilot(false); 222 }, 500); 223 224 } else { 225 removeCopilotDivs(webchatDiv); 226 } 227 } 228 229 } 230 }); 231 // to remove error, we skip endOfConversation renderer 232 const store = WebChat.createStore({}, ({dispatch}) => next => action => { 233 if (action.type === 'DIRECT_LINE/INCOMING_ACTIVITY') { 234 if (action.payload.activity.type === 'endOfConversation') { 235 savedChatId = chatId 236 localStorage.removeItem('pva_token'); 237 238 if (webchatDiv) { 239 webchatDiv.innerHTML = ''; 240 } 241 242 setTimeout(() => { 243 initCopilot(); 244 }, 500); 245 $('.chat__modal').addClass('active') 246 $('.chat__modal-tab').addClass('active') 247 return; 248 } 249 } 250 return next(action); 251 }); 252 if (webchatDiv) { 253 const parent = webchatDiv.parentElement; 254 webchatDiv.remove(); 255 256 const newDiv = document.createElement('div'); 257 newDiv.id = 'webchat'; 258 parent.appendChild(newDiv); 259 webchatDiv = newDiv 260 window.WebChatInstance = WebChat.renderWebChat( 261 { 262 directLine, 263 locale, 264 styleOptions, 265 store 266 }, 267 newDiv 268 ); 269 } 270 console.log(window.WebChat); 271 } catch (error) { 272 console.error('Error rendering Web Chat'); 273 removeCopilotDivs(webchatDiv); 274 } 275 } 276 $('#chat_feedback_form').validate({ 277 submitHandler: function submitHandler(form, event) { 278 event.preventDefault(); 279 const formArray = $(form).serializeArray(); 280 const formData = {}; 281 formArray.forEach(item => { 282 formData[item.name] = item.value; 283 }); 284 285 $.ajax({
286 type: 'POST', 287 url: '/cpanalytics/store', 288 contentType: 'application/json', 289 data: JSON.stringify({ 290 type: 'feedback', 291 data: formData, 292 conversation_id: savedChatId ? savedChatId : chatId, 293 }) 294 }).done(function (response) { 295 toggleChatView(true) 296 297 $('#chat_feedback').val('') 298 $('.chat__modal').removeClass('active') 299 $('.chat__modal-tab').removeClass('active') 300 301 localStorage.removeItem('pva_token'); 302 303 if (webchatDiv) { 304 webchatDiv.innerHTML = ''; 305 } 306 307 setTimeout(() => { 308 initCopilot(); 309 }, 500); 310 }).fail(function (error) { 311 }); 312 } 313 }); 314 $('#chat_leave_feedback').on('click', function (){ 315 $('.chat__modal-choose').removeClass('active') 316 $('.chat__modal-tab').addClass('active') 317 }) 318 $('#chat_back').on('click', function (){ 319 $('.chat__modal').removeClass('active') 320 $('.chat__modal-choose').removeClass('active') 321 }) 322 $('#chat_close').on('click', function (){ 323 $('.chat__modal').removeClass('active') 324 $('.chat__modal-choose').removeClass('active') 325 toggleChatView(true) 326 }) 327 $(document).on('click', function (e) { 328 const $chat = $('#chat-container'); 329 330 if ($chat.css('display') === 'flex') { 331 if ($(e.target).closest('#chat-container').length === 0 && 332 $(e.target).closest('#chat-icon').length === 0) { 333 $('.chat__modal').removeClass('active') 334 $('.chat__modal-choose').removeClass('active') 335 toggleChatView(true) 336 } 337 } 338 }); 339 $('.chat-resize').on('click', function (){ 340 $(this).toggleClass('active') 341 $('.chat-container').toggleClass('chat-container--wide') 342 }) 343 $('.chat-closer').on('click', function (){ 344 toggleChatView() 345 }) 346}); 347 348 349
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.