1if (window._cb === undefined) { 2 var Vera = (function(window, undefined) { 3 let dataLayer = window.dataLayer || []; 4 let baseUrl = window._cbParams.base_url; 5 let timeout = window._cbParams.timeout || 10000; 6 let apiUrl = baseUrl + '/api'; 7 let params = {}; 8 let callVeraStart = false; 9 let Vera = {}; 10 11 let userId = window._cbParams.user_id; 12 let app = window._cbParams.client_id; 13 let region = window._cbParams.region; 14 15 let options = {}; 16 17 if (MediaRecorder.isTypeSupported('audio/webm; codecs=opus')) { 18 options = {mimeType: 'audio/webm; codecs=opus', audioBitsPerSecond: 16000}; 19 } else if (MediaRecorder.isTypeSupported('audio/mp4')) { 20 options = {mimeType: 'audio/mp4', audioBitsPerSecond: 16000}; 21 } 22 23 let mediaRecorder = null; 24 25 let wavesurfers = []; 26 let wsuId = 1; 27 let ws_chatbot = null; 28 29 Vera._noAnswerPending = false; 30 31 Vera.loadChatWindow = function() { 32 33 dataLayer.push({ 34 'event': 'chatbot-display' 35 }); 36 37 let chatWindowNode = document.createElement('div'); 38 chatWindowNode.innerHTML = ` 39 <div id="open-chatbot" class="enabled"></div> 40 <div id="open-chatbot-message"> 41 <a id="chatbot-message-close" href="#"><span class="close-icon"></span></a> 42 <p> 43 Guten Tag!<br />Ich bin der Chatbot der WKO.<br />Wie kann ich Ihnen helfen? 44 </p> 45 </div> 46 <div id="close-chatbot" style="display: none;"></div> 47 <div id="chatbot-window"></div> 48 `; 49 50 document.getElementsByTagName('body')[0].appendChild(chatWindowNode); 51 Vera.jq.ajax({ 52 method: 'GET', 53 url: baseUrl + '/chat-window', 54 contentType: 'text/html', 55 xhrFields: { 56 withCredentials: true 57 }, 58 crossDomain: true, 59 success: function (data) { 60 Vera.jq("#chatbot-window").html(data); 61 Vera.initChatWindow(); 62 } 63 }); 64 } 65 66 Vera.initChatWindow = function() { 67 Vera.jq('#open-chatbot').click(function() { 68 dataLayer.push({ 69 'event': 'chatbot-open' 70 }); 71 if(callVeraStart){ 72 var message = { 73 'type': 'text', 74 'text': 'vera_start' 75 } 76 Vera.ask(message, 'vera_start'); 77 callVeraStart = false;
78 }else{ 79 let $chatBody = Vera.jq('.chatbot-chat-history'); 80 $chatBody.scrollTop($chatBody.prop("scrollHeight")); 81 } 82 83 Vera.jq('#chatbot-window').addClass('open'); 84 Vera.jq('#open-chatbot').hide(); 85 Vera.jq('#close-chatbot').show(); 86 Vera.jq('#open-chatbot-message').hide(); 87 }); 88 89 Vera.jq('#close-chatbot, .close-icon').click(function() { 90 if(ws_chatbot != null) { 91 ws_chatbot.destroy(); 92 } 93 94 Vera.jq('#cancel-audio-message').trigger('click'); 95 96 wavesurfers.forEach(function(waversurfer, index) { 97 if(waversurfer && typeof waversurfer.destroy === 'function'){ 98 waversurfer.stop(); 99 let $playbackBtn = Vera.jq('#wavesurfer-' + index).siblings('.playback-button'); 100 if($playbackBtn.hasClass('pause')){ 101 $playbackBtn.removeClass('pause').addClass('play'); 102 } 103 } 104 }); 105 106 Vera.jq('.text-audio-message.playing').removeClass('playing'); 107 108 Vera.jq('#chatbot-window').removeClass('open'); 109 Vera.jq('#open-chatbot').show(); 110 Vera.jq('#close-chatbot').hide(); 111 }); 112 113 Vera.jq('#chatbot-give-consent').on('click', function() { 114 Vera.jq.ajax({ 115 headers: { 116 'X-CSRF-TOKEN': Vera.jq('#chat-input').find('[name=\'_token\']').first().val() 117 }, 118 method: 'POST', 119 url: apiUrl + '/refresh-consent', 120 data: JSON.stringify({ 121 event: event, 122 userId: userId, 123 app: app, 124 region: region, 125 confirmed: 1 126 }), 127 dataType: 'json', 128 contentType: 'application/json', 129 xhrFields: { 130 withCredentials: true 131 }, 132 crossDomain: true, 133 success: function (response) { 134 if(response.confirmed) { 135 console.log("in"); 136 Vera.jq('#chatbot-window').removeClass('no-consent'); 137 } 138 } 139 }); 140 }); 141 142 Vera.jq('#chatbot-message-close').click(function(e) { 143 e.preventDefault(); 144 e.stopPropagation(); 145 Vera.jq('#open-chatbot-message').hide(); 146 }); 147 148 if(parseInt(window.sessionStorage.getItem("openChatbot")) !== 1 && parseInt(window.sessionStorage.getItem("openMessageAlreadyShown")) !== 1){ 149 setTimeout(function() { 150 Vera.jq('#open-chatbot-message').addClass('active'); 151 window.sessionStorage.setItem("openMessageAlreadyShown", 1); 152 }, 2000); 153 } 154 155 Vera.applySizeClass(); 156 157 Vera.jq(window).resize(function() { 158 Vera.applySizeClass(); 159 }); 160 161 Vera.jq.ajax({ 162 method: 'GET', 163 url: apiUrl + '/check-consent-status', 164 contentType: 'text/html', 165 xhrFields: { 166 withCredentials: true 167 }, 168 crossDomain: true, 169 success: function (data) { 170 if(!data.confirmed) { 171 Vera.jq('#chatbot-window').addClass('no-consent'); 172 } 173 174 Vera.jq.ajax({ 175 method: 'GET', 176 url: apiUrl + '/load-session-history', 177 contentType: 'text/html', 178 xhrFields: { 179 withCredentials: true 180 }, 181 crossDomain: true, 182 success: function (data) { 183 if(data.length > 0){ 184 data.forEach(function(element) { 185 audioVal = Object.values(element.userMessage)[0]?.audioFile ?? null; 186 fileSavedVal = Object.values(element.userMessage)[0]?.file_saved ?? 0; 187 188 if(audioVal && fileSavedVal) { 189 let audioHTML = `<div class="audio-playback"><span class="playback-button play"></span><div class="ws-audio" id="wavesurfer-${wsuId}" data-href="${baseUrl}/${audioVal}"></div></div>`; 190 Vera.showMessages([{ 'type': 'text', 'text': audioHTML }], true); 191 192 wavesurfers[wsuId] = Vera.ws.create({ 193 container: '#wavesurfer-' + wsuId, 194 url: `${baseUrl}/${audioVal}`, 195 height: 60, 196 width: 200, 197 splitChannels: false, 198 waveColor: '#AAA', 199 progressColor: '#000', 200 cursorColor: '#000', 201 cursorWidth: 2, 202 barWidth: 4, 203 barGap: 2, 204 barRadius: 10,
205 mediaControls: false, 206 interact: true, 207 audioRate: 1, 208 autoScroll: true, 209 autoCenter: true, 210 sampleRate: 48000, 211 }); 212 wsuId++; 213 } else { 214 Vera.showMessages(element.userMessage, true, 0, '', false, "global", false); 215 } 216 Vera.showMessages( 217 element.answers, 218 false, 219 element.id, 220 element.feedback, 221 false, 222 element.region, 223 false, 224 element.llmGenerated 225 ); 226 }); 227 }else{ 228 callVeraStart = true; 229 } 230 Vera.initChat(); 231 if(parseInt(window.sessionStorage.getItem("openChatbot")) === 1){ 232 Vera.jq('#open-chatbot').click(); 233 window.sessionStorage.removeItem("openChatbot") 234 } 235 } 236 }); 237 } 238 }); 239 } 240 241 Vera.applySizeClass = function () { 242 let width = Vera.jq(window).innerWidth(); 243 let height = Vera.jq(window).innerHeight(); 244 245 if(width >= 768) { 246 Vera.jq('#chatbot-window').addClass('desktop'); 247 } else { 248 Vera.jq('#chatbot-window').removeClass('desktop'); 249 } 250 } 251 252 Vera.initChat = function() { 253 Vera.jq('#send-user-message').click(function(e) { 254 e.preventDefault(); 255 e.stopPropagation(); 256 257 var $messageInput = Vera.jq('#user-message-input'); 258 var textMessage = Vera.jq.trim($messageInput.text()); 259 260 if (textMessage && textMessage.length > 0) { 261 var message = { 262 'type': 'text', 263 'text': Vera.escapeMessage(textMessage) 264 } 265 Vera.ask(message, ''); 266 267 $messageInput.text('').focus(); 268 } 269 }); 270 271 Vera.jq('#audio-input-message').click(function(e) { 272 e.preventDefault(); 273 e.stopPropagation(); 274 275 if(ws_chatbot != null){ 276 ws_chatbot.destroy(); 277 } 278 279 Vera.jq('.text-audio-message.playing').removeClass('playing'); 280 281 let cancelTimeout; 282 let canceled = false; 283 var isPaused = false; 284 285 Vera.jq('#send-audio-message, #cancel-audio-message').off('click'); 286 287 navigator.mediaDevices.getUserMedia({ audio: true }) 288 .then(function(stream) { 289 const mediaRecorder = new MediaRecorder(stream, options); 290 const audioChunks = []; 291 292 mediaRecorder.start(); 293 294 Vera.jq('.chatbot-message-input').hide(); 295 Vera.jq('.chatbot-audio-container').css('display', 'flex'); 296 297 let startTime = new Date().getTime(); 298 let recordingTime = setInterval(function() { 299 if(!isPaused) { 300 let currentTime = new Date().getTime(); 301 let elapsedTime = currentTime - startTime; 302 let seconds = Math.floor(elapsedTime / 1000) % 60; 303 let minutes = Math.floor(elapsedTime / 1000 / 60) % 60; 304 305 Vera.jq('.chatbot-audio-container .audio-duration').html(minutes + ":" + ((seconds < 10 ? "0" : "") + seconds)); 306 } 307 }, 1000); 308 309 mediaRecorder.addEventListener("dataavailable", function(event) { 310 audioChunks.push(event.data); 311 }); 312 313 Vera.jq('#send-audio-message').on('click', function() { 314 e.preventDefault(); 315 e.stopPropagation(); 316 mediaRecorder.stop(); 317 }); 318 319 Vera.jq('#cancel-audio-message').on('click', function() { 320 e.preventDefault(); 321 e.stopPropagation(); 322 323 clearTimeout(cancelTimeout); 324 mediaRecorder.stop(); 325 canceled = true; 326 }); 327 328 mediaRecorder.addEventListener("start", function() { 329 cancelTimeout = setTimeout(() => { 330 if (!canceled) { 331 isPaused = true; 332 mediaRecorder.pause(); 333 let errorMessageHtml = `<div class="chatbot-message
333C chatbot-audio-error"> 334 <div class="audio-error"> 335 <span class="audio-error-icon"></span> 336 <span class="audio-error-message"> 337 Aufnahmezeit überschritten. Bitte halten Sie Spracheingaben unter 30 Sekunden. 338 </span> 339 </div> 340 </div>`; 341 Vera.jq('.chatbot-chat-history').append(errorMessageHtml); 342 } 343 }, 30000); 344 }); 345 346 mediaRecorder.addEventListener("stop", function() { 347 clearTimeout(cancelTimeout); 348 clearInterval(recordingTime); 349 350 Vera.jq('.chatbot-audio-container .audio-duration').html('0:00'); 351 Vera.jq('.chatbot-audio-container').css('display', 'none'); 352 Vera.jq('.chatbot-message-input').show(); 353 354 let audioBlob = {}; 355 356 if (MediaRecorder.isTypeSupported('audio/webm; codecs=opus')) { 357 audioBlob = new Blob(audioChunks, { type: 'audio/webm; codecs=opus' }); 358 } else if (MediaRecorder.isTypeSupported('audio/mp4')) { 359 audioBlob = new Blob(audioChunks, { type: 'audio/mp4' }); 360 } 361 362 const formData = new FormData(); 363 formData.append('audio', audioBlob); 364 365 mediaRecorder.stream.getAudioTracks().forEach(function(track){track.stop();}); 366 367 if(!canceled) { 368 let loadingMessage = `<div class="chatbot-messageC chatbot-audio-loading"> 369 <div class="loading-container"> 370 <span class="audio-loading"></span> 371 </div> 372 </div>`; 373 374 Vera.jq('.chatbot-chat-history').append(loadingMessage); 375 376 Vera.jq.ajax({ 377 url: apiUrl + '/speech-to-text', 378 type: 'POST', 379 data: formData, 380 contentType: false, 381 processData: false, 382 headers: { 383 'X-CSRF-TOKEN': Vera.jq('#chat-input').find('[name=\'_token\']').first().val() 384 }, 385 dataType: 'json', 386 xhrFields: { 387 withCredentials: true 388 }, 389 crossDomain: true, 390 success: function(response) { 391 Vera.jq('.chatbot-audio-loading').remove(); 392 if (response.transcription) { 393 let $fileSaved = Vera.jq('#audio-available'); 394 let $messageInput = Vera.jq('#user-message-input'); 395 let $audio = Vera.jq('#audio'); 396 397 $fileSaved.val(response.fileSaved) 398 $messageInput.text(response.transcription) 399 $audio.val(response.audioUrl) 400 401 Vera.jq('#send-user-message').trigger('click') 402 } else { 403 let errorMessageHtml = `<div class="chatbot-message
403C chatbot-audio-error"> 404 <div class="audio-error"> 405 <span class="audio-error-icon"></span> 406 <span class="audio-error-message"> 407 ${response.error} 408 </span> 409 </div> 410 </div>`; 411 Vera.jq('.chatbot-chat-history').append(errorMessageHtml); 412 } 413 }, 414 error: function(xhr, status, error) { 415 Vera.jq('.chatbot-audio-loading').remove(); 416 if(xhr.status === 419){ 417 Vera.updateCsrf(message, event) 418 } 419 420 let err = xhr.error || `Ein unbekannter Fehler ist aufgetreten. Bitte Versuchen sie es in einigen Minuten noch einmal.`; 421 let errorMessageHtml = `<div class="chatbot-messageC chatbot-audio-error"> 422 <div class="audio-error"> 423 <span class="audio-error-icon"></span> 424 <span class="audio-error-message"> 425 ${err} 426 </span> 427 </div> 428 </div>`; 429 Vera.jq('.chatbot-chat-history').append(errorMessageHtml); 430 } 431 }); 432 } 433 }); 434 }) 435 .catch(function(error) { 436 let errorMessageHtml = `<div class="chatbot-messageC chatbot-audio-error"> 437 <div class="audio-error"> 438 <span class="audio-error-icon"></span> 439 <span class="audio-error-message"> 440 Zugriff auf Mikrofon verweigert. Bitte erlauben Sie den Zugriff in den Einstellungen Ihres Geräts. 441 </span> 442 </div> 443 </div>`; 444 Vera.jq('.chatbot-chat-history').append(errorMessageHtml); 445 Vera.jq('.chatbot-audio-container').css('display', 'none'); 446 Vera.jq('.chatbot-message-input').show(); 447 }); 448 }); 449 450 Vera.jq(document).on('click', '.text-audio-message:not(.playing)', function(e) { 451 e.preventDefault(); 452 e.stopPropagation(); 453 454 Vera.jq('#cancel-audio-message').trigger('click'); 455 456 if(ws_chatbot != null) { 457 ws_chatbot.destroy(); 458 } 459 460 wavesurfers.forEach(function(wavesurfer, index) { 461 if(wavesurfer && typeof wavesurfer.stop === 'function') { 462 wavesurfer.stop(); 463 let $playbackBtn = Vera.jq('#wavesurfer-' + index).siblings('.playback-button'); 464 if($playbackBtn.hasClass('pause')){ 465 $playbackBtn.removeClass('pause').addClass('play'); 466 } 467 } 468 }); 469 470 Vera.jq('.text-audio-message').removeClass('playing'); 471 472 let $buttonElem = Vera.jq(this); 473 $buttonElem.addClass('loading'); 474 475 var textMessage = ''; 476 var $textAudioMessage = Vera.jq(this); 477 var $chatMessageContainer = $textAudioMessage.parents('.chatbot-message
477C.chatbot-responseC').first(); 478 479 $chatMessageContainer.find('.chatbot-chat-message').each(function() { 480 var $clone = Vera.jq(this).clone(); 481 $clone.find('.ai-generated-label').remove(); 482 var messageText = $clone.text().trim(); 483 textMessage += messageText + ' '; 484 }); 485 486 $chatMessageContainer.find('.chatbot-quick-reply').each(function() { 487 var quickReplyText = Vera.jq(this).text().trim(); 488 textMessage += quickReplyText + '. '; 489 }); 490 491 textMessage = textMessage.trim(); 492 493 textMessage = textMessage.trim(); 494 textMessage = $chatMessageContainer.html().trim(); 495 496 var $htmlClone = $chatMessageContainer.clone(); 497 $htmlClone.find('.ai-generated-label').remove(); 498 textMessage = $htmlClone.html().trim(); 499 500 if (textMessage.length > 0) { 501 Vera.jq.ajax({ 502 headers: { 503 'X-CSRF-TOKEN': Vera.jq('#chat-input').find('[name=\'_token\']').first().val() 504 }, 505 method: 'POST', 506 url: apiUrl + '/text-to-speech', 507 data: JSON.stringify({ 508 textinput: textMessage, 509 device: (/Mobi|Android/i.test(navigator.userAgent) ? "mobile" : "desktop"), 510 id: $buttonElem.data('id'), 511 user_id: userId, 512 app: app, 513 region: region 514 }), 515 dataType: 'json', 516 contentType: 'application/json', 517 xhrFields: { 518 withCredentials: true 519 }, 520 crossDomain: true, 521 success: function(response) { 522 if (response.audioContent) { 523 var audio = new Audio("data:audio/mp3;base64," + response.audioContent); 524 525 if(ws_chatbot != null) { 526 ws_chatbot.destroy(); 527 } 528 529 $buttonElem.parent().append('<div class="ws-audio" id="wavesurferCB"></div>') 530 531 ws_chatbot = Vera.ws.create({ 532 container: '#wavesurferCB', 533 media: audio, 534 height: 20, 535 width: 200, 536 splitChannels: false, 537 waveColor: '#AAA', 538 progressColor: '#000', 539 cursorColor: '#000', 540 cursorWidth: 2, 541 barWidth: 4, 542 barGap: 2, 543 barRadius: 10, 544 mediaControls: false, 545 interact: true, 546 audioRate: 1, 547 autoScroll: true, 548 autoCenter: true, 549 sampleRate: 48000, 550 }); 551 552 ws_chatbot.playPause(); 553 $buttonElem.removeClass('loading'); 554 $buttonElem.addClass('playing'); 555 556 ws_chatbot.on('finish', () => { 557 ws_chatbot.destroy(); 558 }); 559 560 ws_chatbot.on('destroy', () => { 561 $buttonElem.removeClass('playing'); 562 $buttonElem.parent().find('#wavesurferCB').remove(); 563 }); 564 565 Vera.jq(document).on('click', '.text-audio-message.playing', function(e) { 566 e.preventDefault(); 567 e.stopPropagation(); 568 ws_chatbot.destroy(); 569 }); 570 571 audio.addEventListener("ended", (event) => { 572 $buttonElem.removeClass('playing'); 573 }); 574 575 } else { 576 let errorMessageHtml = `<div class="chatbot-message
576C chatbot-audio-error"> 577 <div class="audio-error"> 578 <span class="audio-error-icon"></span> 579 <span class="audio-error-message"> 580 ${response.error} 581 </span> 582 </div> 583 </div>`; 584 Vera.jq('.chatbot-chat-history').append(errorMessageHtml); 585 $buttonElem.removeClass('loading'); 586 } 587 }, 588 error: function(xhr, status, error) { 589 $buttonElem.removeClass('loading'); 590 if(xhr.status === 419){ 591 Vera.updateCsrf(message, event) 592 } 593 let errorMessageHtml = `<div class="chatbot-messageC chatbot-audio-error"> 594 <div class="audio-error"> 595 <span class="audio-error-icon"></span> 596 <span class="audio-error-message"> 597 Ein unbekannter Fehler ist aufgetreten. Bitte Versuchen sie es in einigen Minuten noch einmal. 598 </span> 599 </div> 600 </div>`; 601 Vera.jq('.chatbot-chat-history').append(errorMessageHtml); 602 } 603 }); 604 } 605 }); 606 607 Vera.jq(document).on('click', "#chatbot-window .chatbot-quick-reply-button:not(.chatbot-quick-reply-outline), #chatbot-window .chatbot-action-list-item", function(e){ 608 e.preventDefault(); 609 e.stopPropagation(); 610 var message = { 611 'type': 'text', 612 'text': Vera.escapeMessage(Vera.jq(this).data("query")) 613 } 614 Vera.ask(message,''); 615 }); 616 617 Vera.jq(document).on('click', ".chatbot-quick-reply-outline", function(e){ 618 e.preventDefault(); 619 e.stopPropagation(); 620 Vera.jq(this).parents(".chatbot-response-quick-replies").find(".chatbot-quick-reply").show(); 621 Vera.jq(this).hide(); 622 }); 623 624 Vera.jq("#user-message-input").on('keypress',function(e) { 625 if(e.which == 13) { 626 e.preventDefault(); 627 Vera.jq("#send-user-message").click(); 628 } 629 }); 630 631 Vera.jq(document).on('click', '.chatbot-feedback-icon-up', function(e){ 632 e.preventDefault(); 633 Vera.jq.ajax({ 634 headers: { 635 'X-CSRF-TOKEN': Vera.jq('#chat-input').find('[name=\'_token\']').first().val() 636 }, 637 method: 'POST', 638 url: apiUrl + '/store-feedback', 639 data: JSON.stringify({ 640 feedback: "positiv", 641 message_id: Vera.jq(this).data("id") 642 }), 643 dataType: 'json', 644 contentType: 'application/json', 645 xhrFields: { 646 withCredentials: true 647 }, 648 crossDomain: true, 649 }); 650 651 Vera.jq(this).addClass("active"); 652 Vera.jq(this).parent().find("span").addClass("disabled"); 653 654 }); 655 656 Vera.jq(document).on('click', '.chatbot-feedback-icon-down', function(e){ 657 e.preventDefault(); 658 Vera.jq.ajax({ 659 headers: { 660 'X-CSRF-TOKEN': Vera.jq('#chat-input').find('[name=\'_token\']').first().val() 661 }, 662 method: 'POST', 663 url: apiUrl + '/store-feedback', 664 data: JSON.stringify({ 665 feedback: "negativ", 666 message_id: Vera.jq(this).data("id") 667 }), 668 dataType: 'json', 669 contentType: 'application/json', 670 xhrFields: { 671 withCredentials: true 672 }, 673 crossDomain: true, 674 }); 675 676 Vera.jq(this).addClass("active"); 677 Vera.jq(this).parent().find("span").addClass("disabled"); 678 679 let $feedbackContent = Vera.jq(this).parents(".chatbot-message
679C").find(".chatbot-message-content").first().clone(); 680 $feedbackContent.find('.ai-generated-label').remove(); 681 $feedbackContent.find('.chatbot-chat-message-last-row').remove(); 682 var message = { 683 'type': 'feedback', 684 'text': 'nein', 685 'feedback_message': "<div class='feedbackmessage'><span class='chatbot-feedback-title'>Feedback zu:</span><div class='chatbot-feedback-body'>" + $feedbackContent.html() + "</div></div>" 686 } 687 Vera.ask(message, 'feedback_start'); 688 }); 689 690 Vera.jq(document).on("click", "#chatbot-window a[target!='_blank']:not(.chatbot-quick-reply-button,.chatbot-action-list-item)", function (e) { 691 e.preventDefault(); 692 dataLayer.push({ 693 "event": "chatbot-link-click", 694 "url": Vera.jq(this).attr("href") 695 }); 696 if(Vera.jq(window).innerWidth() > 992) { 697 window.sessionStorage.setItem("openChatbot", 1); 698 } 699 window.location.href = Vera.jq(this).attr("href"); 700 }); 701 702 Vera.jq(document).on("click", "#chatbot-window a[target='_blank']", function (e) { 703 e.preventDefault(); 704 dataLayer.push({ 705 "event": "chatbot-link-click", 706 "url": Vera.jq(this).attr("href") 707 }); 708 window.open(Vera.jq(this).attr("href"), "_blank"); 709 }); 710 711 Vera.jq(document).on('click', '.playback-button', function() { 712 let wsId = Vera.jq(this).siblings('.ws-audio').attr('id'); 713 let $playbackBtn = Vera.jq(this); 714 715 if(ws_chatbot != null){ 716 ws_chatbot.destroy(); 717 } 718 719 Vera.jq('.text-audio-message.playing').removeClass('playing'); 720 721 wavesurfers.forEach(function(wavesurfer, index) { 722 if(wsId.split('-')[1] != index && wavesurfer && typeof wavesurfer.stop === 'function') { 723 wavesurfer.stop(); 724 let $otherBtn = Vera.jq('#wavesurfer-' + index).siblings('.playback-button'); 725 if($otherBtn.hasClass('pause')){ 726 $otherBtn.removeClass('pause').addClass('play'); 727 } 728 } 729 }); 730 731 wavesurfers[wsId.split('-')[1]].unAll(); 732 wavesurfers[wsId.split('-')[1]].playPause(); 733 $playbackBtn.toggleClass('play').toggleClass('pause'); 734 735 wavesurfers[wsId.split('-')[1]].on('finish', () => { 736 $playbackBtn.toggleClass('play').toggleClass('pause'); 737 }); 738 }); 739 } 740 741 742 Vera._currentStream = null; 743 744 Vera.cancelActiveStream = function() { 745 if (Vera._currentStream) { 746 Vera._currentStream.close(); 747 Vera._currentStream = null; 748 } 749 750 751 const $leftover = Vera.jq('.chatbot-writing-animation-elem'); 752 if ($leftover.length > 0) { 753 $leftover.remove(); 754 } 755 756 } 757 758 Vera.openStreamTalk = function(originalMessage, streamId, historyId) { 759 760 Vera.cancelActiveStream(); 761 762 const animHtml = '<div class="chatbot-messageC chatbot-responseC chatbot-writing-animation-elem">'+ 763 ' <div class="chatbot-text-to-speech">'+ 764 ' <span class="text-audio-message" data-id="0"></span>'+ 765 ' </div>'+ 766 ' <div class="chatbot-chat-message first-message last-message">'+ 767 ' <div class="chatbot-message-content">'+ 768 ' <div class="ai-generated-label">'+ 769 ' KI-generierte Antwort'+ 770 ' </div>'+ 771 ' <div class="streaming-text"></div>'+ 772 ' <span class="cursor-blink">|</span>'+ 773 ' </div>'+ 774 ' </div>'+ 775 '</div>'; 776 777 Vera.jq('.chatbot-chat-history').append(animHtml); 778 const $anim = Vera.jq('.chatbot-writing-animation-elem').last(); 779 const $streamingText = $anim.find('.streaming-text'); 780 const $cursor = $anim.find('.cursor-blink'); 781 const $ttsButton = $anim.find('.text-audio-message'); 782 783 const $chatBody = Vera.jq('.chatbot-chat-history'); 784 $chatBody.animate({ 785 scrollTop: $chatBody.prop("scrollHeight") 786 }, 300); 787 788 const paramsQS = new URLSearchParams({ 789 question: (originalMessage || '').trim(), 790 message: (originalMessage || '').trim(), 791 region: (typeof region !== 'undefined' ? region : 'global'), 792 current_page: window.location.href, 793 stream_id: (streamId || ''), 794 history_id: (historyId || ''), 795 event: '', 796 feedbackMessage: '', 797 userId: (typeof userId !== 'undefined' ? userId : ''), 798 app: (typeof app !== 'undefined' ? app : ''), 799 device: (/Mobi|Android/i.test(navigator.userAgent) ? "mobile" : "desktop") 800 }); 801 802 const streamUrl = apiUrl + '/stream-talk?' + paramsQS.toString(); 803 804 const source = new EventSource(streamUrl, {withCredentials: true}); 805 Vera._currentStream = source; 806 807 let fullText = ''; 808 let renderedText = ''; 809 let streamClosed = false;
810 let renderInterval = null; 811 let messageCount = 0; 812 let timeoutId = null; 813 let lastRenderTime = Date.now(); 814 815 const renderNextChars = function() { 816 const now = Date.now(); 817 818 if (renderedText.length >= fullText.length) { 819 return; 820 } 821 822 const waitingChars = fullText.length - renderedText.length; 823 let charsToAdd = 1; 824 825 if (waitingChars > 100) { 826 charsToAdd = 5; 827 } else if (waitingChars > 50) { 828 charsToAdd = 3; 829 } else if (waitingChars > 20) { 830 charsToAdd = 2; 831 } else { 832 charsToAdd = 1; 833 } 834 835 renderedText = fullText.substring(0, renderedText.length + charsToAdd); 836 $streamingText.html(renderedText.replace(/\n/g, '<br>')); 837 838 lastRenderTime = now; 839 840 const $chatBody = Vera.jq('.chatbot-chat-history'); 841 const isNearBottom = $chatBody.scrollTop() + $chatBody.innerHeight() >= $chatBody.prop("scrollHeight") - 100; 842 843 if (isNearBottom) { 844 $chatBody.scrollTop($chatBody.prop("scrollHeight")); 845 } 846 }; 847 848 renderInterval = setInterval(renderNextChars, 20); 849 850 let closeStream = function(reason) { 851 if (streamClosed) { 852 return; 853 } 854 streamClosed = true; 855 856 if (timeoutId) { 857 clearTimeout(timeoutId); 858 timeoutId = null; 859 } 860 861 if (renderInterval) { 862 clearInterval(renderInterval); 863 renderInterval = null; 864 } 865 866 if (reason !== 'error in data' && reason !== 'timeout') { 867 // Final render 868 renderedText = fullText; 869 $streamingText.html(fullText.replace(/\n/g, '<br>')); 870 } else { 871 $streamingText.parents('.chatbot-messageC.chatbot-responseC').first().remove(); 872 } 873 874 // Remove cursor 875 $cursor.remove(); 876 $anim.removeClass('chatbot-writing-animation-elem'); 877 878 // Update TTS button with history_id 879 if (historyId && historyId > 0) { 880 $ttsButton.attr('data-id', historyId); 881 882 var cDate = new Date(); 883 var cMinute = cDate.getMinutes(); 884 var cHour = cDate.getHours(); 885 if (cMinute < 10) cMinute = '0' + cMinute; 886 if (cHour < 10) cHour = '0' + cHour; 887 888 var feedbackHtml = '<div class="chatbot-chat-message-last-row">' + 889 ' <div class="chatbot-feedback-icons">' + 890 ' <span class="chatbot-feedback-icon-up" data-id="' + historyId + '"></span>' + 891 ' <span class="chatbot-feedback-icon-down" data-id="' + historyId + '"></span>' + 892 ' </div>' + 893 ' <div class="chatbot-chat-message-time"><span>' + cHour + ':' + cMinute + '</span></div>' + 894 '</div>'; 895 896 $anim.find('.chatbot-message-content').append(feedbackHtml); 897 } else { 898 $ttsButton.hide(); 899 } 900 901 // Close EventSource 902 try { 903 source.close(); 904 905 if (Vera._currentStream === source) { 906 Vera._currentStream = null; 907 } 908 } catch(e){ 909 910 } 911 912 // Scroll to bottom 913 const $chatBody = Vera.jq('.chatbot-chat-history'); 914 $chatBody.scrollTop($chatBody.prop("scrollHeight")); 915 916 // Auto-play TTS on mobile if enabled 917 if (historyId && historyId > 0 && Vera.jq(window).innerWidth() < 768) { 918 setTimeout(function() { 919 $ttsButton.trigger('click'); 920 }, 300); 921 } 922 923 }; 924 925 source.onmessage = function(ev) { 926 messageCount++; 927 928 if (!ev.data || streamClosed) { 929 return; 930 } 931 932 try { 933 let parsed = JSON.parse(ev.data); 934 935 if (parsed && typeof parsed === 'object') { 936 937 if (parsed.type === 'word') { 938 fullText += parsed.data; 939 return; 940 } 941 942 else if (parsed.type === 'feedback_ready') { 943 historyId = parsed.history_id; 944 return; 945 } 946 947 else if (parsed.type === 'done') { 948 closeStream('received [DONE]'); 949 return; 950 } 951 952 else if (parsed.type === 'error') { 953 let err = parsed.message || 'Ein Fehler ist aufgetreten.'; 954 let errorMessageHtml = `<div class="chatbot-message
954C chatbot-audio-error"> 955 <div class="audio-error"> 956 <span class="audio-error-icon"></span> 957 <span class="audio-error-message"> 958 ${err} 959 </span> 960 </div> 961 </div>`; 962 Vera.jq('.chatbot-chat-history').append(errorMessageHtml); 963 964 fullText = err; 965 closeStream('error in data'); 966 return; 967 } 968 } 969 } catch (e) { 970 971 } 972 }; 973 974 const emptyErrorText = 'Die Verbindung wurde unterbrochen. Bitte versuchen Sie es erneut.'; 975 976 source.onerror = function(e) { 977 978 if (!streamClosed) { 979 if (fullText === '') { 980 fullText = emptyErrorText; 981 } 982 closeStream('onerror'); 983 } 984 }; 985 986 timeoutId = setTimeout(function() { 987 if (fullText === '') { 988 let errorMessageHtml = `<div class="chatbot-messageC chatbot-audio-error"> 989 <div class="audio-error"> 990 <span class="audio-error-icon"></span> 991 <span class="audio-error-message"> 992 ${emptyErrorText} 993 </span> 994 </div> 995 </div>`; 996 Vera.jq('.chatbot-chat-history').append(errorMessageHtml); 997 998 fullText = emptyErrorText; 999 } 1000 1001 if (!streamClosed && Vera._currentStream === source) { 1002 closeStream('timeout'); 1003 } 1004 }, 120000); 1005 }; 1006 1007 Vera.ask = function(message, event = '', hideUserMessage = false) { 1008 1009 if (Vera._currentStream) { 1010 Vera.cancelActiveStream(); 1011 } 1012 1013 let $audio = Vera.jq('#audio'); 1014 let $fileSaved = Vera.jq('#audio-available'); 1015 let audioVal = $audio.val() ?? null; 1016 let fileSavedVal = $fileSaved.val(); 1017 $audio.val(null); 1018 1019 if (event === '') { 1020 if (!hideUserMessage) { 1021 if (audioVal && parseInt(fileSavedVal)) { 1022 let audioHTML = `<div class="audio-playback"><span class="playback-button play"></span><div class="ws-audio" id="wavesurfer-${wsuId}" data-href="${baseUrl}/${audioVal}"></div></div>`; 1023 Vera.showMessages([{ 'type': 'text', 'text': audioHTML }], true); 1024 1025 wavesurfers[wsuId] = Vera.ws.create({ 1026 container: '#wavesurfer-' + wsuId, 1027 url: `${baseUrl}/${audioVal}`, 1028 height: 60, 1029 width: 200, 1030 splitChannels: false, 1031 waveColor: '#AAA', 1032 progressColor: '#000', 1033 cursorColor: '#000', 1034 cursorWidth: 2, 1035 barWidth: 4, 1036 barGap: 2, 1037 barRadius: 10, 1038 mediaControls: false, 1039 interact: true, 1040 audioRate: 1, 1041 autoScroll: true, 1042 autoCenter: true, 1043 sampleRate: 48000, 1044 }); 1045 wsuId++; 1046 } else { 1047 Vera.showMessages([message], true); 1048 } 1049 $fileSaved.val(null); 1050 } 1051 } 1052 1053 let fb = ""; 1054 if (message.hasOwnProperty('feedback_message')) { 1055 fb = message.feedback_message; 1056 } 1057 1058 Vera.jq.ajax({ 1059 headers: { 1060 'X-CSRF-TOKEN': Vera.jq('#chat-input').find('[name=\'_token\']').first().val() 1061 }, 1062 method: 'POST', 1063 url: apiUrl + '/talk', 1064 data: JSON.stringify({ 1065 event: event, 1066 message: message.text, 1067 feedbackMessage: fb, 1068 userId: userId, 1069 app: app, 1070 audio: audioVal, 1071 fileSaved: fileSavedVal, 1072 device: /Mobi|Android/i.test(navigator.userAgent) ? "mobile" : "desktop", 1073 region: region 1074 }), 1075 dataType: 'json', 1076 contentType: 'application/json', 1077 xhrFields: { 1078 withCredentials: true 1079 }, 1080 crossDomain: true, 1081 success: function (data, textStatus, jqXHR) { 1082 Vera._noAnswerPending = false;
1083 if (event === '' && message && message.text) { 1084 Vera._lastUserText = message.text; 1085 } 1086 1087 if (data && data.stream === true) { 1088 const originalText = Vera._lastUserText || message.text || ''; 1089 Vera.openStreamTalk(originalText, data.stream_id, data.history_id); 1090 return; 1091 } 1092 1093 // Normal answer processing 1094 if (data && data.success !== -1) { 1095 const answersEmpty = (!data.answers || data.answers.length === 0); 1096 if (answersEmpty) { 1097 if (!Vera._noAnswerPending) { 1098 Vera._noAnswerPending = true; 1099 const originalText = Vera._lastUserText || message.text || ''; 1100 if (String(originalText).trim() !== '') { 1101 Vera.ask({ type: 'text', text: originalText }, 'no_answer', true); 1102 Vera._noAnswerPending = false; 1103 } 1104 } 1105 1106 return; 1107 } 1108 1109 Vera.showMessages(data.answers, false, data.id); 1110 1111 var $chatBody = Vera.jq('.chatbot-chat-history'); 1112 var maxTop = $chatBody.prop("scrollHeight") - $chatBody.innerHeight(); 1113 var nearBottom = $chatBody.scrollTop() >= (maxTop - 120); 1114 $chatBody.scrollTop($chatBody.prop("scrollHeight")); 1115 1116 if (audioVal && parseInt(fileSavedVal) && Vera.jq(window).innerWidth() < 768) { 1117 let $lastAudioButton = Vera.jq('.chatbot-responseC').last().find('.text-audio-message:not(.playing)'); 1118 if ($lastAudioButton.length > 0) { 1119 $lastAudioButton.trigger('click'); 1120 } 1121 } 1122 } 1123 }, 1124 error: function (jqXHR) { 1125 if (jqXHR.status === 419) { 1126 Vera.updateCsrf(message, event); 1127 } 1128 } 1129 }); 1130 } 1131 1132 window.addEventListener('beforeunload', function() { 1133 if (typeof Vera !== 'undefined' && typeof Vera.cancelActiveStream === 'function') { 1134 Vera.cancelActiveStream(); 1135 } 1136 }); 1137 1138 document.addEventListener('visibilitychange', function() { 1139 if (document.hidden && typeof Vera !== 'undefined' && typeof Vera.cancelActiveStream === 'function') { 1140 Vera.cancelActiveStream(); 1141 } 1142 }); 1143 1144 1145 Vera.showMessages = function(messages, isUserMessage, id = 0, feedback = '', showAnim = true, historyRegion = "global", isCurrent = true, isLlmGenerated = false) { 1146 var returnedMessages = []; 1147 if(messages.length > 0){ 1148 var textToSpeechElem = `<div class="chatbot-text-to-speech"><span href="#" class="text-audio-message" data-id="${id}"></span></div>`; 1149 var $messageContainer = Vera.jq('<div class="chatbot-messageC ' + (isUserMessage ? 'chatbot-user-message' : 'chatbot-responseC') + '">' + (isUserMessage ? '' : textToSpeechElem) + '</div>'); 1150 var isFirstMessage = true; 1151 var isLastMessage = false; 1152 for (var i=0; i<messages.length; i++) { 1153 1154 if(i === 0){ 1155 isFirstMessage = true 1156 } 1157 if (i === messages.length-1){ 1158 isLastMessage = true; 1159 } 1160 1161 var message = messages[i] 1162 if (message === undefined || (message.type == 'text' && message.text == '')) { 1163 break; 1164 } 1165 1166 var cDate = new Date(); 1167 var cMinute = cDate.getMinutes(); 1168 var cHour = cDate.getHours(); 1169 1170 if (cMinute < 10) { 1171 cMinute = '0' + cMinute; 1172 } 1173 1174 if (cHour < 10) { 1175 cHour = '0' + cHour; 1176 } 1177 1178 $messageContent = Vera.jq('<div class="chatbot-message-content"></div>'); 1179 if (isLlmGenerated) { 1180 $messageContent.append('<div class="ai-generated-label">KI-generierte Antwort</div>'); 1181 } 1182 $quickreplyContent = null; 1183 1184 var additionalClass = ""; 1185 if(isFirstMessage && message.type != 'feedback'){ 1186 additionalClass+= "first-message"; 1187 } 1188 if (isLastMessage){ 1189 additionalClass+= " last-message"; 1190 } 1191 var $messageElem = Vera.jq('<div class="chatbot-chat-message '+additionalClass+'"></div>'); 1192 switch (message.type) { 1193 case 'answer': 1194 var answer = message.payload.global; 1195 if(historyRegion !== "global" && message.payload[historyRegion] !== undefined){ 1196 answer = message.payload[historyRegion]; 1197 } 1198 1199 if(message.payload[region] !== undefined){ 1200 answer = message.payload[region]; 1201 } 1202 1203 if(answer !== undefined){ 1204 switch(answer.type){ 1205 case 'text': 1206 $messageContent.append(Vera.jq('<p>
1206' + answer.text + '</p>')); 1207 break; 1208 case 'html': 1209 $messageContent.append(Vera.jq(answer.html)); 1210 break; 1211 case 'quickReply': 1212 let isList = false; 1213 quickReply = answer.quickReply; 1214 if(quickReply.hasOwnProperty('listenansicht')){ 1215 if(quickReply.listenansicht){ 1216 isList = true; 1217 } 1218 }else{ 1219 isList = false; 1220 } 1221 if(isList){ 1222 $quickreplyList = Vera.jq('<div class="chatbot-action-list"></div>'); 1223 1224 for (var i=0; i<quickReply.options.length; i++) { 1225 $quickreplyList.append(Vera.jq('<a class="chatbot-action-list-item" href="#" data-query="'+ quickReply.options[i] +'">' + quickReply.options[i] + '</a>')); 1226 } 1227 $messageContent.append($quickreplyList); 1228 $messageElem.addClass("chatbot-response-action-list"); 1229 }else{ 1230 quickReply = answer.quickReply; 1231 $quickreplyContent = Vera.jq('<div class="chatbot-response-quick-replies"></div>'); 1232 for (var j=0; j<quickReply.options.length; j++) { 1233 $quickreplyContent.append(Vera.jq('<div class="chatbot-quick-reply"' +((j > 2) && 'style="display:none;"')+ '><a class="chatbot-quick-reply-button" href="#" data-query="'+ quickReply.options[j] +'">' + quickReply.options[j] + '</a></div>')); 1234 if (j == 2 && quickReply.options.length > 3){ 1235 $quickreplyContent.append(Vera.jq('<div class="chatbot-quick-reply"><a class="chatbot-quick-reply-button chatbot-quick-reply-outline" href="#">Weitere Themen â¦</a></div>')); 1236 } 1237 } 1238 1239 $messageContent.append(Vera.jq('<p>' + quickReply.title + '</p>')); 1240 if(quickReply.description){ 1241 $messageContent.append(Vera.jq(quickReply.description)); 1242 } 1243 } 1244 1245 break; 1246 case 'video': 1247 var youtubeVideoId = Vera.getYoutubeVideoId(answer.video.url); 1248 var accessibilityText = answer.video.title; 1249 $messageContent.append(Vera.jq('<p>' + answer.video.title + '</p>')); 1250 if(answer.video.description){ 1251 $messageContent.append(Vera.jq(answer.video.description)); 1252 } 1253 $messageContent.append('<iframe src="//www.youtube.com/embed/' + youtubeVideoId + '" title="' + accessibilityText + '" frameborder="0" allowfullscreen></iframe>'); 1254 break; 1255 } 1256 } 1257 1258 break; 1259 case 'text': 1260 $messageContent.append(Vera.jq('<p>' + message.text + '</p>')); 1261 break; 1262 case 'htmlText': 1263 $messageContent.append(Vera.jq(message.htmlText)); 1264 break; 1265 case 'feedback': 1266 if (message.feedback_message) { 1267 $messageContent.append(Vera.jq(message.feedback_message)); 1268 } 1269 break; 1270 default: 1271 $messageContent.append('<p>Ich habe leider keine passende Antwort gefunden.</p>'); 1272 } 1273 1274 if(isUserMessage) { 1275 $messageContent.append('<div class="chatbot-chat-message-last-row"><div class="chatbot-chat-message-time"><span>' + cHour + ':' + cMinute + '</span></div></div>'); 1276 $messageElem.append($messageContent); 1277 } else { 1278 $messageElem.append($messageContent); 1279 } 1280 1281 if($messageElem.html() !== '<div class="chatbot-message-content"></div>') { 1282 $messageContainer.append($messageElem); 1283 } 1284 1285 if($quickreplyContent){ 1286 $messageContainer.append($quickreplyContent); 1287 } 1288 1289 let showFeedback = true; 1290 if(message.hasOwnProperty("payload")){ 1291 let tmpAnswer = null; 1292 if (message.payload && Object.prototype.hasOwnProperty.call(message.payload, 'global')) { 1293 tmpAnswer = message.payload.global; 1294 } 1295 if (isCurrent) { 1296 if (message.payload && Object.prototype.hasOwnProperty.call(message.payload, region)) { 1297 tmpAnswer = message.payload[region]; 1298 } 1299 } else { 1300 if (message.payload && Object.prototype.hasOwnProperty.call(message.payload, historyRegion)) { 1301 tmpAnswer = message.payload[historyRegion]; 1302 } 1303 } 1304 1305 if (tmpAnswer && typeof tmpAnswer === 'object' && Object.prototype.hasOwnProperty.call(tmpAnswer, 'showFeedback')) { 1306 if (!tmpAnswer.showFeedback) { 1307 showFeedback = false;
1308 } 1309 } 1310 } 1311 1312 if(!isUserMessage && isLastMessage && id !== null && showFeedback){ 1313 $messageContent.append('<div class="chatbot-chat-message-last-row">' + 1314 ' <div class="chatbot-feedback-icons">' + 1315 ' <span class="chatbot-feedback-icon-up '+ (feedback === "positiv" ? "active" : "") + (feedback !== "" && feedback !== null ? " disabled" : "") + '" data-id="' + id + '"></span>' + 1316 ' <span class="chatbot-feedback-icon-down '+ (feedback === "negativ" ? "active" : "") + (feedback !== "" && feedback !== null ? " disabled" : "") +'" data-id="' + id + '"></span>' + 1317 ' </div>' + 1318 ' <div class="chatbot-chat-message-time"><span>' + cHour + ':' + cMinute + '</span></div>' + 1319 ' </div>'); 1320 } 1321 1322 if($messageElem.html() !== '<div class="chatbot-message-content"></div>'){ 1323 returnedMessages[i] = $messageElem; 1324 } 1325 var $chatBody = Vera.jq('.chatbot-chat-history'); 1326 if (isUserMessage && showAnim) { 1327 $chatBody.animate({ scrollTop: $chatBody.prop("scrollHeight") }, 200); 1328 } else if(isUserMessage) { 1329 $chatBody.scrollTop($chatBody.prop("scrollHeight")); 1330 } 1331 } 1332 1333 if($messageContainer.text().trim()){ 1334 Vera.jq('.chatbot-chat-history').append($messageContainer); 1335 } else { 1336 const isFirstMessage = Vera.jq('.chatbot-chat-history .chatbot-responseC').length === 0; 1337 const timeSinceStream = Date.now() - (Vera._lastStreamTime || 0); 1338 const hasActiveStream = Vera._currentStream !== null; 1339 const isStreamingMode = isLlmGenerated || hasActiveStream; 1340 } 1341 } 1342 1343 return returnedMessages; 1344 } 1345 1346 Vera.getYoutubeVideoId = function(url) { 1347 var regExp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=)([^#&?]*).*/; 1348 var match = url.match(regExp); 1349 1350 return (match && match[2].length === 11) 1351 ? match[2] 1352 : null; 1353 } 1354 1355 Vera.updateCsrf = function(message, event) { 1356 Vera.jq.ajax({ 1357 method: 'GET', 1358 url: apiUrl + '/refresh-csrf', 1359 contentType: 'text/html', 1360 xhrFields: { 1361 withCredentials: true 1362 }, 1363 crossDomain: true, 1364 success: function (data) { 1365 Vera.jq("input[name=_token]").val(data); 1366 Vera.jq('[name="csrf_token"]').val(data); 1367 Vera.ask(message, event, true); 1368 } 1369 }); 1370 } 1371 1372 Vera.escapeMessage = function (str) { 1373 var div = document.createElement('div'); 1374 div.appendChild(document.createTextNode(str)); 1375 return div.innerHTML; 1376 } 1377 1378 function loadScript(url, callback) { 1379 let script = document.createElement('script'); 1380 script.async = true; 1381 script.src = url; 1382 1383 let entry = document.getElementById('cb-script'); 1384 entry.parentNode.insertBefore(script, entry); 1385 1386 script.onload = script.onreadystatechange = function() { 1387 let rdyState = script.readyState; 1388 1389 if (!rdyState || /complete|loaded/.test(script.readyState)) { 1390 callback(); 1391 1392 script.onload = null; 1393 script.onreadystatechange = null; 1394 } 1395 } 1396 } 1397 1398 function loadStylesheet(url) { 1399 let link = document.createElement('link'); 1400 link.rel = 'stylesheet'; 1401 link.type = 'text/css'; 1402 link.href = url; 1403 1404 let entry = document.getElementsByTagName('script')[0]; 1405 entry.parentNode.insertBefore(link, entry); 1406 } 1407 1408 function isCssReady(callback) { 1409 let testElem = document.createElement('div'); 1410 testElem.id = 'vera-css-ready'; 1411 testElem.style = 'color: #ffffff'; 1412 1413 let entry = document.getElementsByTagName('script')[0]; 1414 entry.parentNode.insertBefore(testElem, entry); 1415 1416 (function poll() { 1417 let node = document.getElementById('vera-css-ready'); 1418 let value; 1419 1420 if (window.getComputedStyle) { 1421 value = document.defaultView.getComputedStyle(testElem, null).getPropertyValue('color'); 1422 } else if (node.currentStyle) { 1423 value = node.currentStyle.color; 1424 } 1425 1426 if (value && value === 'rgb(186, 218, 85)' || value.toLocaleLowerCase() === '#bada55') { 1427 callback(); 1428 node.parentNode.removeChild(node); 1429 } else { 1430 setTimeout(poll, 500); 1431 } 1432 })(); 1433 } 1434 1435 function getScriptUrl() { 1436 let scripts = document.getElementsByTagName('script'); 1437 let element; 1438 let src; 1439 1440 for (let i=0; i<scripts.length; i++) { 1441 element = scripts[i]; 1442 src = element.src; 1443 if (src && /(.*)embed\.js/.test(src)) { 1444 return src; 1445 } 1446 } 1447 return null; 1448 } 1449 1450 function getQueryParams(query) { 1451 let args = query.split('&'); 1452 let params = {}; 1453 let pair; 1454 let key; 1455 let value; 1456 1457 function decode(string) { 1458 return decodeURIComponent(string || "").replace('+', ' '); 1459 } 1460 1461 for (let i=0; i<args.length; i++) { 1462 pair = args[i].split('='); 1463 key = decode(pair[0]); 1464 value = decode(pair[1]); 1465 params[key] = value; 1466 } 1467 return params; 1468 } 1469 1470 function getWidgetParams() { 1471 let url = getScriptUrl(); 1472 let params = getQueryParams(url.replace(/^.*\?/, '')); 1473 return params; 1474 } 1475 1476 function loadSupportingFiles(callback) { 1477 callback(); 1478 } 1479 1480 loadSupportingFiles(function() { 1481 params = getWidgetParams(); 1482 1483 var curDate = new Date(); 1484 var timestamp = "" + curDate.getFullYear() + (curDate.getMonth() + 1) + curDate.getDate(); 1485 loadStylesheet(baseUrl + '/chat/css/style.css?t=' + timestamp); 1486 isCssReady(function() { 1487 setTimeout(function() { 1488 loadScript(baseUrl + '/chat/js/jquery.embed.min.js', function() { 1489 Vera.loadChatWindow(); 1490 }); 1491 loadScript(baseUrl + '/chat/js/wavesurfer.embed.min.js', function() { 1492 1493 }); 1494 }, timeout); 1495 }); 1496 }); 1497 1498 return Vera; 1499 })(window); 1500} else { 1501 var Vera = window._cb; 1502} 1503 1504window._cb = Vera;
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.