PageSourceSearch

https://chatbot.wko.at/chat/js/embed.js

js wko.at collected 2026-09-24 06:59:58 UTC 69,160 bytes, 1,504 lines download raw bytes

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.