1// ***************************************************************************************************** 2// ******* speak2web UNIVERSAL VOICE SEARCH *********** 3// ******* Get your subscription at *********** 4// ******* https://speak2web.com/plugin#plans *********** 5// ******* Need support? https://speak2web.com/support *********** 6// ******* Licensed GPLv2+ *********** 7//****************************************************************************************************** 8window.onload = (event) => { 9 (function () { 10 'use strict'; 11 12 // Auto timeout duration to stop listening Mic 13 var uvsMicListenAutoTimeoutDuration = null; 14 15 if (typeof (vs.uvsMicListenTimeoutDuration) != 'undefined' && vs.uvsMicListenTimeoutDuration !== null) { 16 uvsMicListenAutoTimeoutDuration = parseInt(vs.uvsMicListenTimeoutDuration); 17 uvsMicListenAutoTimeoutDuration = isNaN(uvsMicListenAutoTimeoutDuration) ? 8 : uvsMicListenAutoTimeoutDuration; 18 } else { 19 uvsMicListenAutoTimeoutDuration = 8; 20 } 21 22 uvsMicListenAutoTimeoutDuration = (uvsMicListenAutoTimeoutDuration < 8) ? 8 : uvsMicListenAutoTimeoutDuration; 23 uvsMicListenAutoTimeoutDuration = (uvsMicListenAutoTimeoutDuration > 20) ? 20 : uvsMicListenAutoTimeoutDuration; 24 uvsMicListenAutoTimeoutDuration = uvsMicListenAutoTimeoutDuration * 1000; 25 26 // Function to clear mic reset timeout 27 function uvsClearMicResetTimeout() { 28 try { 29 if (window.uvsMicTimeoutIdentifier) { 30 clearTimeout(window.uvsMicTimeoutIdentifier); 31 window.uvsMicTimeoutIdentifier = null; 32 } 33 } catch (err) { 34 // Do nothing for now 35 } 36 } 37 38 // sanitize alpha-numeric css values to get numeric value 39 function getNumber(number) { 40 number = parseInt(number, 10); 41 return isNaN(number) || number === null || typeof (number) === 'undefined' ? 0 : number; 42 } 43 44 // Function to check if any mic already listening 45 function uvsAnyOtherMicListening(uvsExceptionBtnId = null) { 46 var uvsOneOfMicListening = false; 47 try { 48 var uvsAllMicButtons = document.querySelectorAll('button.universal-voice-search-button'); 49 50 if (typeof (uvsAllMicButtons) == 'undefined' 51 || uvsAllMicButtons === null 52 || uvsExceptionBtnId == null) { return uvsOneOfMicListening; } 53 54 for (var uvsI = 0; uvsI < uvsAllMicButtons.length; uvsI++) { 55 var uvsClassNames = uvsAllMicButtons[uvsI].className; 56 var uvsBtnId = uvsAllMicButtons[uvsI].getAttribute('id'); 57 58 if (!(typeof (uvsClassNames) != 'undefined' && uvsClassNames.trim() != '')) continue; 59 60 if (uvsClassNames.indexOf('listening') != -1 && uvsExceptionBtnId != uvsBtnId) { 61 uvsOneOfMicListening = true; 62 break; 63 } 64 } 65 } catch (err) { 66 uvsOneOfMicListening = false; 67 } 68 69 return uvsOneOfMicListening; 70 } 71 72 // Clear any pre-existing timeouts 73 uvsClearMicResetTimeout(); 74 75 var inputWrapperSelectorsToSeek = [ 76 'form[role=search]', 77 'form[class=searchform]', 78 'form[class=search_form]', 79 'form[class=search-form]', 80 'form[class=searchForm]' 81 ]; 82 var searchInputSelectorsToSeek = [ 83 'input[name=s]', 84 'input[name=search]', 85 'input[name=find]', 86 'input[type=search]', 87 'input[class=search-field]', 88 'input[class=search_field]', 89 'input[class=searchfield]', 90 'input[class=searchField]',
91 'input[id=search]', 92 'input[id=search-field]', 93 'input[id=search_field]', 94 'input[id=searchfield]', 95 'input[id=searchField]' 96 ]; 97 98 var uvsMicEventToListen = 'click'; 99 100 // Detect Android OS 101 var ua = navigator.userAgent.toLowerCase(); 102 var isAndroid = ua.indexOf("android") > -1; 103 104 var recordTimer = null; 105 106 var uvsAllFormsOnPage = document.querySelectorAll('form');// Get all forms on a page 107 var speechInputWrappers = []; 108 109 // Seek and get forms with search ability 110 try { 111 for (var vdnI = 0; vdnI < inputWrapperSelectorsToSeek.length; vdnI++) { 112 speechInputWrappers = document.querySelectorAll(inputWrapperSelectorsToSeek[vdnI]); 113 114 if (speechInputWrappers.length > 0) { break; } 115 } 116 } catch (err) { speechInputWrappers = []; } 117 118 // Override 'speechInputWrappers' to cover missing forms from page 119 if (speechInputWrappers.length < uvsAllFormsOnPage.length) { 120 speechInputWrappers = null; 121 speechInputWrappers = uvsAllFormsOnPage; 122 } 123 124 let formElementForWidget = null; 125 126 [].forEach.call(speechInputWrappers, function (speechInputWrapper, index) { 127 var inputEl = null; 128 var recognizing = false; 129 130 try { 131 // Preserve first form on page and it's input element for widget 132 if (index == 0) { 133 formElementForWidget = speechInputWrapper; 134 } 135 136 // Get input field intented for search feature on page 137 for (var uvsI = 0; uvsI < searchInputSelectorsToSeek.length; uvsI++) { 138 inputEl = speechInputWrapper.querySelector(searchInputSelectorsToSeek[uvsI]); 139 140 if (inputEl !== null) { break; } 141 } 142 143 // Get submit button 144 let formSubmitBtnEl = speechInputWrapper.querySelector("input[type=submit]"); 145 146 if (formSubmitBtnEl === null) { 147 formSubmitBtnEl = speechInputWrapper.querySelector("button[type=submit]"); 148 } 149 150 if (formSubmitBtnEl !== null) { 151 speechInputWrapper.addEventListener('submit', function (submitEvent) { 152 153 // If mic is listening then abort form submition 154 if (recognizing == true) { 155 submitEvent.preventDefault(); 156 } 157 }, false); 158 159 // Remove any overlapping icon from submit button of search form 160 try { 161 let submitButtonChildNodes = formSubmitBtnEl.querySelectorAll('img, svg'); 162 163 for (let j = 0; j < submitButtonChildNodes.length; j++) { 164 let submitBtnChildNode = submitButtonChildNodes[j]; 165 submitBtnChildNode.classList.add('uvs-hide-element'); 166 } 167 } catch (err) { 168 // do nothing for now 169 } 170 } 171 } catch (err) { inputEl = null; } 172 173 // If search input field not found then continue 174 if (null === inputEl) { return; } 175 176 try { 177 // Try to show the form temporarily so we can calculate the sizes 178 var speechInputWrapperStyle = speechInputWrapper.getAttribute('style'); 179 var havingInlineStyle = (typeof (speechInputWrapperStyle) != 'undefined' 180 && speechInputWrapperStyle !== null && speechInputWrapperStyle.trim() != '') ? true : false; 181 speechInputWrapperStyle = (havingInlineStyle) ? speechInputWrapperStyle + ';' : ''; 182 speechInputWrapper.setAttribute('style', speechInputWrapperStyle + 'display: block !important'); 183 //speechInputWrapper.classList.add('universal-voice-search-wrapper'); 184 speechInputWrapper.classList.add('uvs-sanitize-form-wrapper'); 185 186 // Add some markup as a button to the search form 187 var micBtn = document.createElement('button'); 188 micBtn.setAttribute('type', 'button'); 189 micBtn.setAttribute('class', 'universal-voice-search-button'); 190 micBtn.setAttribute('id', 'universal-voice-search-button' + index); 191 micBtn.appendChild(document.createTextNode(universal_voice_search.button_message)); 192 193 // Add mic image icon 194 var uvsMicIcon = document.createElement('img'); 195 uvsMicIcon.setAttribute('src', vs.uvsImagesPath + 'uvs_mic.svg'); 196 uvsMicIcon.setAttribute('class', 'uvs-mic-image'); 197 198 micBtn.appendChild(uvsMicIcon); 199 200 var inputHeight = getNumber(inputEl.offsetHeight);// Get search input height 201 var buttonSize = getNumber(0.8 * inputHeight); 202 203 // Set default mic button size to 35px when button size c
203alculated to 0 or unknown 204 if (getNumber(buttonSize) == 0) { inputHeight = buttonSize = 35; } 205 206 var micbtnPositionTop = getNumber(0.1 * inputHeight); 207 208 // Size and position of complete mic button 209 var inlineStyle = 'top: ' + micbtnPositionTop + 'px; '; 210 inlineStyle += 'height: ' + buttonSize + 'px !important; '; 211 inlineStyle += 'width: ' + buttonSize + 'px !important; '; 212 inlineStyle += 'z-index: 999 !important; margin-left: 3px !important; border-radius: 50% !important; border: 2px solid #ffff !important;'; 213 micBtn.setAttribute('style', inlineStyle); 214 215 // Create Wrapper to wrap around input search field like a elastic band 216 var wrapper = document.createElement('div'); 217 wrapper.setAttribute('style', speechInputWrapperStyle + 'display: inline-block !important'); 218 let inputCurrentStyle = window.getComputedStyle(inputEl); 219 wrapper.setAttribute('class', 'uvs-mic-band'); 220 wrapper.setAttribute('onclick', 'return false'); 221 wrapper.style.width = inputCurrentStyle.width; 222 inputEl.insertAdjacentElement('beforebegin', wrapper);
222// Place wrapper before input search field 223 224 // Set parent element's (parent of inputEl) display stack order higher 225 // To handle overlapped submit button on mic icon 226 var parentEl = inputEl.parentNode.nodeName; 227 228 if (typeof (parentEl) != 'undefined' && parentEl !== null && parentEl.length != 0) { 229 parentEl = parentEl.toLowerCase(); 230 231 if (parentEl != 'form') { 232 inputEl.parentNode.style.zIndex = 1; 233 } 234 } 235 236 // Append search input field element inside a wrapper band 237 wrapper.appendChild(inputEl); 238 239 // Place mic button/icon exact before search input field element 240 inputEl.insertAdjacentElement('beforebegin', micBtn); 241 inputEl.setAttribute('style', speechInputWrapperStyle + 'width: 100% !important;'); 242 inputEl.classList.add('uvs-mic-band'); 243 244 // Reset form style again 245 speechInputWrapper.setAttribute('style', speechInputWrapperStyle); 246 247 // Setup recognition 248 var finalTranscript = ''; 249 var final_transcript = ""; 250 var ignore_onend; 251 252 if ('webkitSpeechRecognition' in window && uvsClientInfo['chrome'] === true) { 253 var recognition = new webkitSpeechRecognition(); 254 recognition.continuous = true; 255 recognition.interimResults = true; 256 257 recognition.onstart = function () { 258 recognizing = true; 259 }; 260 261 recognition.onerror = function (event) { 262 micBtn.classList.remove('listening'); 263 recognizing = false; 264 265 if (event.error == 'no-speech') { 266 inputEl.placeholder = uvsMessages['unableToHear']; 267 268 // Play 'notAudible' playback 269 uvsAudioPlayer.configure(uvsAlternativeResponse['notAudible']); 270 uvsAudioPlayer.play(); 271 272 ignore_onend = true; 273 } 274 if (event.error == 'audio-capture') { 275 uvsAudioPlayer.configure(uvsAlternativeResponse['micConnect']); 276 uvsAudioPlayer.play(); 277 278 inputEl.placeholder = uvsMessages['micNotAccessible']; 279 ignore_onend = true; 280 } 281 if (event.error == 'not-allowed') { 282 uvsAudioPlayer.configure(uvsAlternativeResponse['micConnect']); 283 uvsAudioPlayer.play(); 284 285 inputEl.placeholder = uvsMessages['browserDenyMicAccess']; 286 micBtn.style.setProperty("color", "white"); 287 ignore_onend = true; 288 } 289 }; 290 291 function processEnd() { 292 recognizing = false; 293 294 if (ignore_onend) { return; } 295 296 finalTranscript = final_transcript; 297 micBtn.classList.remove('listening'); 298 micBtn.style.setProperty("color", "white"); 299 300 if (typeof (finalTranscript) != 'undefined' && finalTranscript.length != 0) { 301 inputEl.value = final_transcript; 302 303 // Play 'basic' playback 304 uvsAudioPlayer.configure(uvsAlternativeResponse['basic'], function () { speechInputWrapper.submit(); }); 305 uvsAudioPlayer.play(); 306 } else { 307 inputEl.placeholder = uvsMessages['ask']; 308 } 309 }; 310 311 recognition.onend = function () { 312 if (isAndroid) { 313 processEnd(); 314 } 315 }; 316 317 recognition.onresult = function (event) { 318 let interim_transcript = ''; 319 320 if (typeof (event.results) == 'undefined') { 321 recognition.onend = null; 322 recognition.stop(); 323 inputEl.placeholder = uvsMessages['unableToHear']; 324 325 // Play 'micConnect' playback 326 uvsAudioPlayer.configure(uvsAlternativeResponse['micConnect']); 327 uvsAudioPlayer.play(); 328 329 return; 330 } 331 332 for (var i = event.resultIndex; i < event.results.length; ++i) { 333 if (event.results[i].isFinal) { 334 final_transcript = event.results[i][0].transcript; 335 336 if (isAndroid == false) { 337 processEnd(); 338 recognition.stop(); 339 } 340 } else { 341 interim_transcript += event.results[i][0].transcript; 342 inputEl.value = interim_transcript; 343 } 344 } 345 }; 346 347 micBtn.addEventListener(uvsMicEventToListen, function (event) { 348 // micBtn.onclick = function (event) { 349 if (uvsAnyOtherMicListening(micBtn.getAttribute('id')) === true) return; 350 351 if (recognizing) { 352 // To clear pre-existing mic reset timeout if any. (Based on duration from settings) 353 uvsClearMicResetTimeout(); 354 355 // Stop ongoing playback if nay 356 if (uvsAudioPlayer.isPlaying()) { 357 uvsAudioPlayer.stop(); 358 } 359 360 if (isAndroid == false) { 361 processEnd(); 362 recognition.stop(); 363 } 364 } else { 365 micBtn.classList.add('listening'); 366 event.preventDefault(); 367 368 // Stop ongoing playback if nay 369 if (uvsAudioPlayer.isPlaying()) { 370 uvsAudioPlayer.stop(); 371 } 372 373 inputEl.value = finalTranscript = ''; 374 recognizing = true; 375 recognition.lang = !!uvsSttLanguageContext['gcp']['stt'] ? uvsSttLanguageContext['gcp']['langCode'] : 'en-US';; 376 recognition.start(); 377 ignore_onend = false;
378 379 // To clear pre-existing mic reset timeout if any. (Based on duration from settings) 380 uvsClearMicResetTimeout(); 381 382 // To set new mic reset timeout. (Based on duration from settings) 383 window.uvsMicTimeoutIdentifier = setTimeout(function () { 384 let updatedClassList = micBtn.classList; 385 386 if (updatedClassList && updatedClassList.contains('listening')) { 387 micBtn.click(); 388 } 389 }, uvsMicListenAutoTimeoutDuration); 390 } 391 }); 392 } else { 393 //CODE FOR BROWSERS THAT DO NOT SUPPORT STT NATIVLY 394 // MUST USE THE BUILT IN MICROPHONE 395 micBtn.addEventListener(uvsMicEventToListen, function (event) { 396 /** 397 * Audio element's play method must be invoked in exact influence of user gesture to avoid auto play restriction 398 * 399 */ 400 if ( 401 uvsClientInfo.ios === true 402 || (uvsClientInfo.iosSafari && !uvsClientInfo.chrome && !uvsClientInfo.firefox && !uvsClientInfo.edge) 403 || (uvsClientInfo.windows && uvsClientInfo.firefox) 404 ) { 405 uvsAudioPlayer.configure(uvsSilenceSoundPath); 406 uvsAudioPlayer.play(); 407 } 408 409 if (uvsAnyOtherMicListening(micBtn.getAttribute('id')) === true) return; 410 411 // Deny recording if microphone is not accessible 412 if (!uvsAudioRecorder || !uvsAudioContext) { 413 uvsInitAudio(function (a) { 414 if (!uvsAudioRecorder || !uvsAudioContext) { 415 alert(uvsMessages['cantAccessMicrophone']); 416 return false; 417 } else { 418 listenEvent(); 419 } 420 }); 421 } else { 422 listenEvent(); 423 } 424 425 function listenEvent() { 426 // If API system key is unavailable then acknowledge service unavailability and stop voice navigation. 427 if (!(typeof (vs.uvsXApiKey) != 'undefined' && vs.uvsXApiKey !== null)) { 428 // Play 'unavailable' playback 429 uvsAudioPlayer.configure(uvsAlternativeResponse['unavailable']); 430 uvsAudioPlayer.play(); 431 432 return false; 433 } 434 435 // User ending recording by clicking back mic 436 if (recognizing) { 437 // To clear pre-existing mic reset timeout if any. (Based on duration from settings) 438 uvsClearMicResetTimeout(); 439 440 // Stop recorder 441 uvsAudioRecorder.stop(); 442 443 // Stop access to audio resource 444 uvsStopAudio(); 445 446 // Stop ongoing playback if nay 447 if (uvsAudioPlayer.isPlaying()) { 448 uvsAudioPlayer.stop(); 449 } 450 451 //replace recording with mic icon 452 micBtn.classList.remove('listening'); 453 454 micBtn.style.setProperty("color", "white"); 455 inputEl.placeholder = uvsMessages['transcribeText']; 456 457 uvsAudioRecorder.getBuffers(function (buffers) { 458 if (!!uvsSttLanguageContext['gcp']['stt']) { 459 uvsAudioRecorder.exportMonoWAV(function (blob) { 460 uvsAudioRecorder.convertBlobToBase64(blob).then(function (resultedBase64) { 461 uvsGcpStt(resultedBase64).then(function (transcriptResult) { 462 inputEl.value = transcriptResult; 463 464 // Play 'basic' playback 465 uvsAudioPlayer.configure(uvsAlternativeResponse['basic'], function () { 466 speechInputWrapper.submit(); 467 }); 468 uvsAudioPlayer.play(); 469 }).catch(function (error) { 470 // Play 'notAudible' playback 471 uvsAudioPlayer.configure(uvsAlternativeResponse['notAudible']); 472 uvsAudioPlayer.play(); 473 474 inputEl.placeholder = uvsMessages['ask']; 475 }) 476 }).catch(function (error) { 477 // Play 'notAudible' playback 478 uvsAudioPlayer.configure(uvsAlternativeResponse['notAudible']); 479 uvsAudioPlayer.play(); 480 481 inputEl.placeholder = uvsMessages['ask']; 482 }); 483 }); 484 } else { 485 // Play 'notAudible' playback 486 uvsAudioPlayer.configure(uvsAlternativeResponse['notAudible']); 487 uvsAudioPlayer.play(); 488 489 inputEl.placeholder = uvsMessages['ask']; 490 } 491 }); 492 493 recognizing = false;
494 return; 495 496 } else {// User started recording by clicking mic 497 micBtn.classList.add('listening'); 498 event.preventDefault(); 499 500 // Stop ongoing playback if nay 501 if (uvsAudioPlayer.isPlaying()) { 502 uvsAudioPlayer.stop(); 503 } 504 505 inputEl.value = finalTranscript = ''; 506 507 recognizing = true; 508 uvsAudioRecorder.clear(); 509 uvsAudioRecorder.record(micBtn); 510 511 // To clear pre-existing mic reset timeout if any. (Based on duration from settings) 512 uvsClearMicResetTimeout(); 513 514 // To set new mic reset timeout. (Based on duration from settings) 515 window.uvsMicTimeoutIdentifier = setTimeout(function () { 516 let updatedClassList = micBtn.classList; 517 518 if (updatedClassList && updatedClassList.contains('listening')) { 519 micBtn.click(); 520 } 521 }, uvsMicListenAutoTimeoutDuration); 522 } 523 } 524 }, false); 525 } 526 } catch (err) { /* do nothing */ } 527 }); 528 529 // Load floating mic with search bar 530 //############################# Floating mic - Widget ################################### 531 let uvsDocFragment = document.createDocumentFragment(); 532 // Create root/widget wrapper 533 let uvsWidgetWrapper = document.createElement('div'); 534 535 let uvsWrapperMicPositionClass = 'uvs-widget-wrapper-middle-right'; 536 let uvsChatWrapperMicPositionClass = 'uvs-widget-chat-wrapper-middle-right'; 537 let uvsMicPosition = vs.uvsSelectedMicPosition ? vs.uvsSelectedMicPosition.toLowerCase() : 'middle right';
538 539 switch (uvsMicPosition) { 540 case 'middle left': 541 uvsWrapperMicPositionClass = 'uvs-widget-wrapper-middle-left'; 542 uvsChatWrapperMicPositionClass = 'uvs-widget-chat-wrapper-middle-left'; 543 break; 544 case 'top right': 545 uvsWrapperMicPositionClass = 'uvs-widget-wrapper-top-right'; 546 uvsChatWrapperMicPositionClass = 'uvs-widget-chat-wrapper-top-right'; 547 break; 548 case 'top left': 549 uvsWrapperMicPositionClass = 'uvs-widget-wrapper-top-left'; 550 uvsChatWrapperMicPositionClass = 'uvs-widget-chat-wrapper-top-left'; 551 break; 552 case 'bottom right': 553 uvsWrapperMicPositionClass = 'uvs-widget-wrapper-bottom-right'; 554 uvsChatWrapperMicPositionClass = 'uvs-widget-chat-wrapper-bottom-right'; 555 break; 556 case 'bottom left': 557 uvsWrapperMicPositionClass = 'uvs-widget-wrapper-bottom-left'; 558 uvsChatWrapperMicPositionClass = 'uvs-widget-chat-wrapper-bottom-left'; 559 break; 560 default: 561 uvsWrapperMicPositionClass = 'uvs-widget-wrapper-middle-right'; 562 uvsChatWrapperMicPositionClass = 'uvs-widget-chat-wrapper-middle-right'; 563 } 564 565 uvsWidgetWrapper.setAttribute('class', 'uvs-widget-wrapper ' + uvsWrapperMicPositionClass + ''); 566 567 // Create chat wrapper 568 let uvsWidgetChatWrapper = document.createElement('div'); 569 uvsWidgetChatWrapper.setAttribute('class', 'uvs-widget-chat-wrapper ' + uvsChatWrapperMicPositionClass + ''); 570 571 // ############################ Widget Fields (Input section) ############################ 572 // Create widget text field and mic (Input Section) 573 let uvsWidgetField = document.createElement('div'); 574 uvsWidgetField.setAttribute('class', 'uvs-widget-field'); 575 576 // Create mic icon wrapper 577 let uvsWidgetMic = document.createElement('a'); 578 uvsWidgetMic.setAttribute('id', 'uvsWidgetMic'); 579 uvsWidgetMic.setAttribute('class', 'uvs-widget-button'); 580 581 // Create and append mic icon/image to mic wrapper 582 let uvsWidgetMicImg = document.createElement('img'); 583 uvsWidgetMicImg.setAttribute('src', vs.uvsImagesPath + 'uvs-widget-mic-black.svg'); 584 uvsWidgetMic.appendChild(uvsWidgetMicImg); 585 586 // Create button wrapper next to input text field 587 let uvsWidgetSearchBtn = document.createElement('a'); 588 uvsWidgetSearchBtn.setAttribute('id', 'uvsWidgetSearchBtn'); 589 590 // Create and append search button to button wrapper 591 let uvsWidgetSearchBtnEl = document.createElement('button'); 592 uvsWidgetSearchBtnEl.setAttribute('class', 'uvs-widget-form-submit-btn'); 593 uvsWidgetSearchBtnEl.setAttribute('type', 'submit'); 594 uvsWidgetSearchBtnEl.setAttribute('alt', 'Go'); 595 uvsWidgetSearchBtnEl.setAttribute('title', 'Search'); 596 uvsWidgetSearchBtn.appendChild(uvsWidgetSearchBtnEl); 597 598 // Create form for widget 599 let uvsWidgetForm = document.createElement('form'); 600 uvsWidgetForm.setAttribute("class", "uvs-widget-form"); 601 602 if (formElementForWidget !== null) { 603 uvsWidgetForm.action = formElementForWidget.action; 604 uvsWidgetForm.method = formElementForWidget.method; 605 } else { 606 uvsWidgetForm.action = uvsGetCurrentHostURL() + '/'; 607 uvsWidgetForm.method = "get"; 608 } 609 610 // Create input text field 611 let uvsWidgetSearch = document.createElement('input'); 612 uvsWidgetSearch.setAttribute('id', 'uvsWidgetSearch'); 613 uvsWidgetSearch.setAttribute('class', 'uvs-widget-search uvs-widget-search-text'); 614 uvsWidgetSearch.setAttribute('name', 'uvs-widget-search'); 615 uvsWidgetSearch.setAttribute('placeholder', uvsWidgetMessages['placeholder']); 616 uvsWidgetSearch.setAttribute('name', 's'); 617 618 uvsWidgetForm.appendChild(uvsWidgetSearch); 619 uvsWidgetForm.appendChild(uvsWidgetSearchBtn); 620 621 // Append mic and form to widget field section (input section) 622 uvsWidgetField.appendChild(uvsWidgetMic); 623 uvsWidgetField.appendChild(uvsWidgetForm); 624 625 // Append chat header, chat conversation and input fields to widget chat wrapper 626 uvsWidgetChatWrapper.appendChild(uvsWidgetField); 627 628 // ################################ Widget Toggle button ######################### 629 // Create a widget toggle button wrapper 630 let uvsWidgetToggleButton = document.createElement('a'); 631 632 // Create toggle button icon element 633 let uvsWidgetIcon = document.createElement('div'); 634 635 // Create a pulse effect it's show when user trigger stt 636 let uvsWidgetPulseEffect = document.createElement('span'); 637 uvsWidgetPulseEffect.setAttribute('id', 'uvsWidgetPulseEffect'); 638 639 if (vs.uvsFloatingMic && vs.uvsFloatingMic === 'yes') { 640 uvsWidgetToggleButton.setAttribute('id', 'uvsWidgetToggleButton'); 641 uvsWidgetToggleButton.setAttribute('class', 'uvs-widget-button'); 642 uvsWidgetIcon.setAttribute('class', 'uvs-widget-icon uvs-widget-toggle-button uvs-toggle-btn-mic'); 643 // Append toggle button icon to toggle button wrapper 644 uvsWidgetToggleButton.appendChild(uvsWidgetIcon); 645 } 646 647 // Append chat wrapper and toggle button to widget wrapper 648 uvsWidgetWrapper.appendChild(uvsWidgetChatWrapper); 649 uvsWidgetWrapper.appendChild(uvsWidgetPulseEffect); 650 uvsWidgetWrapper.appendChild(uvsWidgetToggleButton); 651 652 // Append widget to body 653 uvsDocFragment.appendChild(uvsWidgetWrapper); 654 document.body.appendChild(uvsDocFragment); 655 656 // Listen event to show/hide widget 657 uvsWidgetToggleButton.addEventListener('click', function (event) { 658 uvsToggleWidgetElements(); 659 }); 660 661 /*############################# Widget mic handling ################################*/
662 // Setup recognition 663 let widgetFinalTranscript = ''; 664 let widgetRecognizing = false; 665 let widget_final_transcript = ""; 666 let widget_ignore_onend; 667 668 /** 669 * Function for add pulse animation in elementor mic 670 * 671 */ 672 function uvsElementorMicPulseAnimation(uvsElementorMicElement) { 673 let size = 0, left = 0; 674 if (uvsElementorMicElement.clientHeight >= 80) { 675 size = uvsElementorMicElement.clientHeight + 15; 676 left = -(size / 6); 677 } if (uvsElementorMicElement.clientHeight >= 60) { 678 size = uvsElementorMicElement.clientHeight + 12; 679 left = -(size / 5); 680 } else if (uvsElementorMicElement.clientHeight >= 30) { 681 size = uvsElementorMicElement.clientHeight + 10; 682 left = -(size / 4); 683 } else { 684 size = uvsElementorMicElement.clientHeight + 8; 685 left = -(size / 3.5); 686 } 687 688 689 const uvsPulse = document.createElement('div'); 690 uvsPulse.setAttribute('id', 'pulse'); 691 uvsPulse.setAttribute('class', 'pulse-color'); 692 uvsPulse.style.width = size + 'px'; 693 uvsPulse.style.height = size + 'px'; 694 uvsPulse.style.left = left + 'px'; 695 696 const uvsPulseRate = document.createElement('div'); 697 uvsPulseRate.setAttribute('id', 'pulse-rate'); 698 uvsPulseRate.setAttribute('class', 'pulse-color'); 699 uvsPulseRate.style.width = size + 'px'; 700 uvsPulseRate.style.height = size + 'px'; 701 uvsPulseRate.style.left = left + 'px'; 702 703 return { uvsPulse, uvsPulseRate }; 704 } 705 706 function enableElementor() { 707 const uvsFloatingMic = document.getElementById('flt-mic') 708 if (uvsFloatingMic != null) { 709 const uvsElementorMicColor = uvsFloatingMic.getElementsByClassName('my-icon-wrapper')[0].getElementsByTagName('i')[0]; 710 const uvsPulseItem = uvsElementorMicPulseAnimation(uvsElementorMicColor); 711 if ('webkitSpeechRecognition' in window && uvsClientInfo['chrome'] === true) { 712 let widgetRecognition = new webkitSpeechRecognition(); 713 widgetRecognition.continuous = true; 714 widgetRecognition.interimResults = true; 715 716 widgetRecognition.onstart = function () { 717 widgetRecognizing = true; 718 }; 719 720 widgetRecognition.onerror = function (event) { 721 uvsFloatingMic.classList.remove('listening'); 722 widgetRecognizing = false; 723 uvsElementorMicColor.classList.remove('my-icon-animation-wrapper'); 724 uvsElementorMicColor.removeChild(uvsPulseItem['uvsPulse']); 725 uvsElementorMicColor.removeChild(uvsPulseItem['uvsPulseRate']); 726 727 if (event.error == 'no-speech') { 728 // Play feature unavailable playback 729 uvsAudioPlayer.configure(uvsAlternativeResponse['notAudible']); 730 uvsAudioPlayer.play(); 731 732 widget_ignore_onend = true; 733 uvsWidgetSearch.placeholder = uvsMessages['unableToHear']; 734 } 735 736 if (event.error == 'audio-capture') { 737 uvsAudioPlayer.configure(uvsAlternativeResponse['micConnect']); 738 uvsAudioPlayer.play(); 739 740 widget_ignore_onend = true; 741 uvsWidgetSearch.placeholder = uvsMessages['micNotAccessible']; 742 } 743 744 if (event.error == 'not-allowed') { 745 uvsAudioPlayer.configure(uvsAlternativeResponse['micConnect']); 746 uvsAudioPlayer.play(); 747 748 widget_ignore_onend = true; 749 uvsWidgetSearch.placeholder = uvsMessages['browserDenyMicAccess']; 750 } 751 }; 752 753 function widgetProcessEnd() { 754 widgetRecognizing = false;
755 756 if (widget_ignore_onend) { return; } 757 758 widgetFinalTranscript = widget_final_transcript; 759 uvsFloatingMic.classList.remove('listening'); 760 uvsElementorMicColor.classList.remove('my-icon-animation-wrapper'); 761 uvsElementorMicColor.removeChild(uvsPulseItem['uvsPulse']); 762 uvsElementorMicColor.removeChild(uvsPulseItem['uvsPulseRate']); 763 764 if (typeof (widgetFinalTranscript) != 'undefined' && widgetFinalTranscript.length != 0) { 765 uvsWidgetSearch.value = widgetFinalTranscript; 766 767 // Play 'basic' playback 768 uvsAudioPlayer.configure(uvsAlternativeResponse['basic'], function () { 769 }); 770 uvsAudioPlayer.play(); 771 setTimeout(() => { 772 uvsWidgetForm.submit(); 773 }, 2000); 774 } else { 775 // Play 'notAudible' playback 776 uvsAudioPlayer.configure(uvsAlternativeResponse['notAudible']); 777 uvsAudioPlayer.play(); 778 779 uvsWidgetSearch.placeholder = uvsMessages['ask']; 780 } 781 782 } 783 784 widgetRecognition.onend = function () { 785 if (isAndroid) { widgetProcessEnd(); } 786 }; 787 788 widgetRecognition.onresult = function (event) { 789 let interim_transcript = ''; 790 791 if (typeof (event.results) == 'undefined') { 792 widgetRecognition.onend = null; 793 widgetRecognition.stop(); 794 uvsWidgetSearch.placeholder = uvsMessages['unableToHear']; 795 796 // Play 'micConnect' playback 797 uvsAudioPlayer.configure(uvsAlternativeResponse['micConnect']); 798 uvsAudioPlayer.play(); 799 800 return; 801 } 802 803 let eventResultsLength = event.results.length; 804 805 for (let i = event.resultIndex; i < eventResultsLength; ++i) { 806 if (event.results[i].isFinal) { 807 widget_final_transcript = event.results[i][0].transcript; 808 809 if (isAndroid == false) { 810 widgetProcessEnd(); 811 widgetRecognition.stop(); 812 } 813 } else { 814 interim_transcript += event.results[i][0].transcript; 815 } 816 } 817 }; 818 819 uvsFloatingMic.addEventListener(uvsMicEventToListen, function (event) { 820 if (uvsAnyOtherMicListening(uvsFloatingMic.getAttribute('id'), uvsFloatingMic) === true) return; 821 822 if (widgetRecognizing) { 823 // To clear pre-existing mic reset timeout if any. (Based on duration from settings) 824 uvsClearMicResetTimeout(); 825 826 // Stop ongoing playback if nay 827 if (uvsAudioPlayer.isPlaying()) { 828 uvsAudioPlayer.stop(); 829 } 830 831 if (isAndroid == false) { 832 widgetProcessEnd(); 833 widgetRecognition.stop(); 834 } 835 } else { 836 uvsFloatingMic.classList.add('listening'); 837 uvsElementorMicColor.classList.add('my-icon-animation-wrapper'); 838 uvsElementorMicColor.appendChild(uvsPulseItem['uvsPulse']); 839 uvsElementorMicColor.appendChild(uvsPulseItem['uvsPulseRate']); 840 841 event.preventDefault(); 842 843 // Stop ongoing playback if nay 844 if (uvsAudioPlayer.isPlaying()) { 845 uvsAudioPlayer.stop(); 846 } 847 848 widgetFinalTranscript = ''; 849 widgetRecognizing = true; 850 widgetRecognition.lang = !!uvsSttLanguageContext['gcp']['stt'] ? uvsSttLanguageContext['gcp']['langCode'] : 'en-US'; 851 widgetRecognition.start(); 852 widget_ignore_onend = false;
853 854 // To clear pre-existing mic reset timeout if any. (Based on duration from settings) 855 uvsClearMicResetTimeout(); 856 857 // To set new mic reset timeout. (Based on duration from settings) 858 window.uvsMicTimeoutIdentifier = setTimeout(function () { 859 let updatedClassList = uvsFloatingMic.classList; 860 861 if (updatedClassList && updatedClassList.contains('listening')) { 862 uvsFloatingMic.click(); 863 } 864 }, uvsMicListenAutoTimeoutDuration); 865 } 866 }); 867 } else { 868 //CODE FOR BROWSERS THAT DO NOT SUPPORT STT NATIVLY 869 // MUST USE THE BUILT IN MICROPHONE 870 uvsFloatingMic.addEventListener(uvsMicEventToListen, function (event) { 871 872 /** 873 * Audio element's play method must be invoked in exact influence of user gesture to avoid auto play restriction 874 * 875 */ 876 if ( 877 uvsClientInfo.ios === true 878 || (uvsClientInfo.iosSafari && !uvsClientInfo.chrome && !uvsClientInfo.firefox && !uvsClientInfo.edge) 879 || (uvsClientInfo.windows && uvsClientInfo.firefox) 880 ) { 881 uvsAudioPlayer.configure(uvsSilenceSoundPath); 882 uvsAudioPlayer.play(); 883 } 884 885 if (uvsAnyOtherMicListening(uvsFloatingMic.getAttribute('id'), uvsFloatingMic) === true) return; 886 887 // Deny recording if microphone is not accessible 888 if (!uvsAudioRecorder || !uvsAudioContext) { 889 uvsInitAudio(function (a) { 890 if (!uvsAudioRecorder || !uvsAudioContext) { 891 uvsWidgetSearch.placeholder = uvsMessages['micNotAccessible']; 892 return false; 893 } else { 894 widgetListenEvent(); 895 } 896 }); 897 } else { 898 widgetListenEvent(); 899 } 900 901 function widgetListenEvent() { 902 // If API system key is unavailable then acknowledge service unavailability and stop voice navigation. 903 if (!(typeof (vs.uvsXApiKey) != 'undefined' && vs.uvsXApiKey !== null)) { 904 // Play 'unavailable' playback 905 uvsAudioPlayer.configure(uvsAlternativeResponse['unavailable']); 906 uvsAudioPlayer.play(); 907 908 return false; 909 } 910 911 // User ending recording by clicking back mic 912 if (widgetRecognizing) { 913 // To clear pre-existing mic reset timeout if any. (Based on duration from settings) 914 uvsClearMicResetTimeout(); 915 916 // Stop recorder 917 uvsAudioRecorder.stop(); 918 919 // Stop access to audio resource 920 uvsStopAudio(); 921 922 // Stop ongoing playback if nay 923 if (uvsAudioPlayer.isPlaying()) { 924 uvsAudioPlayer.stop(); 925 } 926 927 //replace recording with mic icon 928 uvsFloatingMic.classList.remove('listening'); 929 uvsElementorMicColor.classList.remove('my-icon-animation-wrapper'); 930 uvsElementorMicColor.removeChild(uvsPulseItem['uvsPulse']); 931 uvsElementorMicColor.removeChild(uvsPulseItem['uvsPulseRate']); 932 933 uvsWidgetSearch.placeholder = uvsMessages['transcribeText']; 934 935 uvsAudioRecorder.getBuffers(function (buffers) { 936 if (!!uvsSttLanguageContext['gcp']['stt']) { 937 uvsAudioRecorder.exportMonoWAV(function (blob) { 938 uvsAudioRecorder.convertBlobToBase64(blob).then(function (resultedBase64) { 939 uvsGcpStt(resultedBase64).then(function (transcriptResult) { 940 uvsWidgetSearch.value = transcriptResult; 941 942 // Play 'basic' playback 943 uvsAudioPlayer.configure(uvsAlternativeResponse['basic'], function () { 944 }); 945 uvsAudioPlayer.play(); 946 setTimeout(() => { 947 uvsWidgetForm.submit(); 948 }, 2000); 949 }).catch(function (error) { 950 // Play 'notAudible' playback 951 uvsAudioPlayer.configure(uvsAlternativeResponse['notAudible']); 952 uvsAudioPlayer.play(); 953 954 uvsWidgetSearch.placeholder = uvsMessages['ask']; 955 }) 956 }).catch(function (error) { 957 // Play 'notAudible' playback 958 uvsAudioPlayer.configure(uvsAlternativeResponse['notAudible']); 959 uvsAudioPlayer.play(); 960 961 uvsWidgetSearch.placeholder = uvsMessages['ask']; 962 }); 963 }); 964 } else { 965 // Play 'notAudible' playback 966 uvsAudioPlayer.configure(uvsAlternativeResponse['notAudible']); 967 uvsAudioPlayer.play(); 968 969 uvsWidgetSearch.placeholder = uvsMessages['ask']; 970 } 971 }); 972 973 widgetRecognizing = false;
974 return; 975 } else {// User started recording by clicking mic 976 uvsFloatingMic.classList.add('listening'); 977 uvsElementorMicColor.classList.add('my-icon-animation-wrapper'); 978 uvsElementorMicColor.appendChild(uvsPulseItem['uvsPulse']); 979 uvsElementorMicColor.appendChild(uvsPulseItem['uvsPulseRate']); 980 981 event.preventDefault(); 982 983 // Stop ongoing playback if nay 984 if (uvsAudioPlayer.isPlaying()) { 985 uvsAudioPlayer.stop(); 986 } 987 988 widgetFinalTranscript = ''; 989 990 widgetRecognizing = true; 991 uvsAudioRecorder.clear(); 992 uvsAudioRecorder.record(uvsFloatingMic); 993 994 // To clear pre-existing mic reset timeout if any. (Based on duration from settings) 995 uvsClearMicResetTimeout(); 996 997 // To set new mic reset timeout. (Based on duration from settings) 998 window.uvsMicTimeoutIdentifier = setTimeout(function () { 999 let updatedClassList = uvsFloatingMic.classList; 1000 1001 if (updatedClassList && updatedClassList.contains('listening')) { 1002 uvsFloatingMic.click(); 1003 } 1004 }, uvsMicListenAutoTimeoutDuration); 1005 } 1006 } 1007 }, false); 1008 } 1009 } 1010 } 1011 1012 /** 1013 * Function for provide simplicity to interct with floating mic using keyboard key between a-z 1014 * 1015 * @param clickType: single or double 1016 */ 1017 function uvsFloatingMicKeyBoardAccess(clickType) { 1018 if (vs.uvsFloatingMic && vs.uvsFloatingMic === 'yes') { 1019 var keyFormSetting = vs.uvsKeyboardSpecialKey == 'OtherKey' ? vs.uvsKeyboardMicSwitch : vs.uvsKeyboardSpecialKey == 'Space' ? ' ' : vs.uvsKeyboardSpecialKey; 1020 var spaceCount = 0; 1021 window.addEventListener('keydown', (event) => { 1022 let target = event.target; 1023 1024 // Check if the event originated from an input field 1025 if (target.tagName === 'INPUT') { 1026 // Ignore keyboard events on input fields 1027 return; 1028 } 1029 1030 if (event.key == keyFormSetting) { 1031 spaceCount++; 1032 event.preventDefault(); 1033 if (spaceCount == 2) { 1034 if (clickType == 'single') { 1035 uvsWidgetToggleButton.click(); 1036 } else if (clickType == 'double') { 1037 if (uvsWidgetChatWrapper.classList.contains('uvs-widget-visible')) { 1038 uvsWidgetMic.click(); 1039 } else { 1040 uvsWidgetToggleButton.click(); 1041 uvsWidgetMic.click(); 1042 } 1043 } 1044 spaceCount = 0; 1045 } 1046 } 1047 }); 1048 } 1049 } 1050 1051 /** 1052 * Function for made a indication on single click 1053 * 1054 * @param action: add for add indication or remove for remove indication 1055 */ 1056 function uvsSingleClickMicEffect(action) { 1057 if (action == 'add') { 1058 uvsWidgetToggleButton.classList.add('listening'); 1059 uvsWidgetToggleButton.classList.add('singleClick'); 1060 uvsWidgetPulseEffect.classList.add('singleClick'); 1061 } else if (action == 'remove') { 1062 uvsWidgetToggleButton.classList.remove('listening'); 1063 uvsWidgetToggleButton.classList.remove('singleClick'); 1064 uvsWidgetPulseEffect.classList.remove('singleClick'); 1065 } 1066 } 1067 1068 function enableSingleClick() { 1069 uvsWidgetWrapper.classList.remove('uvsWidgetChatWrapper'); 1070 if ('webkitSpeechRecognition' in window && uvsClientInfo['chrome'] === true) { 1071 let widgetRecognition = new webkitSpeechRecognition(); 1072 widgetRecognition.continuous = true; 1073 widgetRecognition.interimResults = true; 1074 1075 widgetRecognition.onstart = function () { 1076 widgetRecognizing = true; 1077 }; 1078 1079 widgetRecognition.onerror = function (event) { 1080 uvsSingleClickMicEffect('remove'); 1081 widgetRecognizing = false;
1082 1083 if (event.error == 'no-speech') { 1084 // Play feature unavailable playback 1085 uvsAudioPlayer.configure(uvsAlternativeResponse['notAudible']); 1086 uvsAudioPlayer.play(); 1087 1088 widget_ignore_onend = true; 1089 uvsWidgetSearch.placeholder = uvsMessages['unableToHear']; 1090 } 1091 1092 if (event.error == 'audio-capture') { 1093 uvsAudioPlayer.configure(uvsAlternativeResponse['micConnect']); 1094 uvsAudioPlayer.play(); 1095 1096 widget_ignore_onend = true; 1097 uvsWidgetSearch.placeholder = uvsMessages['micNotAccessible']; 1098 } 1099 1100 if (event.error == 'not-allowed') { 1101 uvsAudioPlayer.configure(uvsAlternativeResponse['micConnect']); 1102 uvsAudioPlayer.play(); 1103 1104 widget_ignore_onend = true; 1105 uvsWidgetSearch.placeholder = uvsMessages['browserDenyMicAccess']; 1106 } 1107 }; 1108 1109 function widgetProcessEnd() { 1110 widgetRecognizing = false; 1111 1112 if (widget_ignore_onend) { return; } 1113 1114 widgetFinalTranscript = widget_final_transcript; 1115 uvsSingleClickMicEffect('remove'); 1116 1117 if (typeof (widgetFinalTranscript) != 'undefined' && widgetFinalTranscript.length != 0) { 1118 uvsWidgetSearch.value = widgetFinalTranscript; 1119 1120 // Play 'basic' playback 1121 uvsAudioPlayer.configure(uvsAlternativeResponse['basic'], function () { 1122 }); 1123 uvsAudioPlayer.play(); 1124 setTimeout(() => { 1125 uvsWidgetForm.submit(); 1126 }, 2000); 1127 } else { 1128 // Play 'notAudible' playback 1129 uvsAudioPlayer.configure(uvsAlternativeResponse['notAudible']); 1130 uvsAudioPlayer.play(); 1131 1132 uvsWidgetSearch.placeholder = uvsMessages['ask']; 1133 } 1134 1135 } 1136 1137 widgetRecognition.onend = function () { 1138 if (isAndroid) { widgetProcessEnd(); } 1139 }; 1140 1141 widgetRecognition.onresult = function (event) { 1142 let interim_transcript = ''; 1143 1144 if (typeof (event.results) == 'undefined') { 1145 widgetRecognition.onend = null; 1146 widgetRecognition.stop(); 1147 uvsWidgetSearch.placeholder = uvsMessages['unableToHear']; 1148 1149 // Play 'micConnect' playback 1150 uvsAudioPlayer.configure(uvsAlternativeResponse['micConnect']); 1151 uvsAudioPlayer.play(); 1152 1153 return; 1154 } 1155 1156 let eventResultsLength = event.results.length; 1157 1158 for (let i = event.resultIndex; i < eventResultsLength; ++i) { 1159 if (event.results[i].isFinal) { 1160 widget_final_transcript = event.results[i][0].transcript; 1161 1162 if (isAndroid == false) { 1163 widgetProcessEnd(); 1164 widgetRecognition.stop(); 1165 } 1166 } else { 1167 interim_transcript += event.results[i][0].transcript; 1168 } 1169 } 1170 }; 1171 1172 uvsWidgetToggleButton.addEventListener(uvsMicEventToListen, function (event) { 1173 if (uvsAnyOtherMicListening(uvsWidgetMic.getAttribute('id'), uvsWidgetMic) === true) return; 1174 1175 if (widgetRecognizing) { 1176 // To clear pre-existing mic reset timeout if any. (Based on duration from settings) 1177 uvsClearMicResetTimeout(); 1178 1179 // Stop ongoing playback if nay 1180 if (uvsAudioPlayer.isPlaying()) { 1181 uvsAudioPlayer.stop(); 1182 } 1183 1184 if (isAndroid == false) { 1185 widgetProcessEnd(); 1186 widgetRecognition.stop(); 1187 } 1188 } else { 1189 uvsSingleClickMicEffect('add'); 1190 event.preventDefault(); 1191 1192 // Stop ongoing playback if nay 1193 if (uvsAudioPlayer.isPlaying()) { 1194 uvsAudioPlayer.stop(); 1195 } 1196 1197 widgetFinalTranscript = ''; 1198 widgetRecognizing = true; 1199 widgetRecognition.lang = !!uvsSttLanguageContext['gcp']['stt'] ? uvsSttLanguageContext['gcp']['langCode'] : 'en-US'; 1200 widgetRecognition.start(); 1201 widget_ignore_onend = false;
1202 1203 // To clear pre-existing mic reset timeout if any. (Based on duration from settings) 1204 uvsClearMicResetTimeout(); 1205 1206 // To set new mic reset timeout. (Based on duration from settings) 1207 window.uvsMicTimeoutIdentifier = setTimeout(function () { 1208 let updatedClassList = uvsWidgetToggleButton.classList; 1209 1210 if (updatedClassList && updatedClassList.contains('listening')) { 1211 uvsWidgetToggleButton.click(); 1212 } 1213 }, uvsMicListenAutoTimeoutDuration); 1214 } 1215 }); 1216 } else { 1217 //CODE FOR BROWSERS THAT DO NOT SUPPORT STT NATIVLY 1218 // MUST USE THE BUILT IN MICROPHONE 1219 uvsWidgetToggleButton.addEventListener(uvsMicEventToListen, function (event) { 1220 /** 1221 * Audio element's play method must be invoked in exact influence of user gesture to avoid auto play restriction 1222 * 1223 */ 1224 if ( 1225 uvsClientInfo.ios === true 1226 || (uvsClientInfo.iosSafari && !uvsClientInfo.chrome && !uvsClientInfo.firefox && !uvsClientInfo.edge) 1227 || (uvsClientInfo.windows && uvsClientInfo.firefox) 1228 ) { 1229 uvsAudioPlayer.configure(uvsSilenceSoundPath); 1230 uvsAudioPlayer.play(); 1231 } 1232 1233 if (uvsAnyOtherMicListening(uvsWidgetToggleButton.getAttribute('id'), uvsWidgetMic) === true) return; 1234 1235 // Deny recording if microphone is not accessible 1236 if (!uvsAudioRecorder || !uvsAudioContext) { 1237 uvsInitAudio(function (a) { 1238 if (!uvsAudioRecorder || !uvsAudioContext) { 1239 uvsWidgetSearch.placeholder = uvsMessages['micNotAccessible']; 1240 return false; 1241 } else { 1242 widgetListenEvent(); 1243 } 1244 }); 1245 } else { 1246 widgetListenEvent(); 1247 } 1248 1249 function widgetListenEvent() { 1250 // If API system key is unavailable then acknowledge service unavailability and stop voice navigation. 1251 if (!(typeof (vs.uvsXApiKey) != 'undefined' && vs.uvsXApiKey !== null)) { 1252 // Play 'unavailable' playback 1253 uvsAudioPlayer.configure(uvsAlternativeResponse['unavailable']); 1254 uvsAudioPlayer.play(); 1255 1256 return false; 1257 } 1258 1259 // User ending recording by clicking back mic 1260 if (widgetRecognizing) { 1261 // To clear pre-existing mic reset timeout if any. (Based on duration from settings) 1262 uvsClearMicResetTimeout(); 1263 1264 // Stop recorder 1265 uvsAudioRecorder.stop(); 1266 1267 // Stop access to audio resource 1268 uvsStopAudio(); 1269 1270 // Stop ongoing playback if nay 1271 if (uvsAudioPlayer.isPlaying()) { 1272 uvsAudioPlayer.stop(); 1273 } 1274 1275 //replace recording with mic icon 1276 uvsSingleClickMicEffect('remove'); 1277 1278 uvsWidgetSearch.placeholder = uvsMessages['transcribeText']; 1279 1280 uvsAudioRecorder.getBuffers(function (buffers) { 1281 if (!!uvsSttLanguageContext['gcp']['stt']) { 1282 uvsAudioRecorder.exportMonoWAV(function (blob) { 1283 uvsAudioRecorder.convertBlobToBase64(blob).then(function (resultedBase64) { 1284 uvsGcpStt(resultedBase64).then(function (transcriptResult) { 1285 uvsWidgetSearch.value = transcriptResult; 1286 1287 // Play 'basic' playback 1288 uvsAudioPlayer.configure(uvsAlternativeResponse['basic'], function () { 1289 }); 1290 uvsAudioPlayer.play(); 1291 setTimeout(() => { 1292 uvsWidgetForm.submit(); 1293 }, 2000); 1294 }).catch(function (error) { 1295 // Play 'notAudible' playback 1296 uvsAudioPlayer.configure(uvsAlternativeResponse['notAudible']); 1297 uvsAudioPlayer.play(); 1298 1299 uvsWidgetSearch.placeholder = uvsMessages['ask']; 1300 }) 1301 }).catch(function (error) { 1302 // Play 'notAudible' playback 1303 uvsAudioPlayer.configure(uvsAlternativeResponse['notAudible']); 1304 uvsAudioPlayer.play(); 1305 1306 uvsWidgetSearch.placeholder = uvsMessages['ask']; 1307 }); 1308 }); 1309 } else { 1310 // Play 'notAudible' playback 1311 uvsAudioPlayer.configure(uvsAlternativeResponse['notAudible']); 1312 uvsAudioPlayer.play(); 1313 1314 uvsWidgetSearch.placeholder = uvsMessages['ask']; 1315 } 1316 }); 1317 1318 widgetRecognizing = false;
1319 return; 1320 } else {// User started recording by clicking mic 1321 uvsSingleClickMicEffect('add'); 1322 event.preventDefault(); 1323 1324 // Stop ongoing playback if nay 1325 if (uvsAudioPlayer.isPlaying()) { 1326 uvsAudioPlayer.stop(); 1327 } 1328 1329 widgetFinalTranscript = ''; 1330 1331 widgetRecognizing = true; 1332 uvsAudioRecorder.clear(); 1333 uvsAudioRecorder.record(uvsWidgetToggleButton); 1334 1335 // To clear pre-existing mic reset timeout if any. (Based on duration from settings) 1336 uvsClearMicResetTimeout(); 1337 1338 // To set new mic reset timeout. (Based on duration from settings) 1339 window.uvsMicTimeoutIdentifier = setTimeout(function () { 1340 let updatedClassList = uvsWidgetToggleButton.classList; 1341 1342 if (updatedClassList && updatedClassList.contains('listening')) { 1343 uvsWidgetToggleButton.click(); 1344 } 1345 }, uvsMicListenAutoTimeoutDuration); 1346 } 1347 } 1348 }, false); 1349 } 1350 } 1351 1352 function disableSingleClick() { 1353 if ('webkitSpeechRecognition' in window && uvsClientInfo['chrome'] === true) { 1354 let widgetRecognition = new webkitSpeechRecognition(); 1355 widgetRecognition.continuous = true; 1356 widgetRecognition.interimResults = true; 1357 1358 widgetRecognition.onstart = function () { 1359 widgetRecognizing = true; 1360 }; 1361 1362 widgetRecognition.onerror = function (event) { 1363 uvsWidgetMic.classList.remove('listening'); 1364 widgetRecognizing = false; 1365 1366 if (event.error == 'no-speech') { 1367 // Play feature unavailable playback 1368 uvsAudioPlayer.configure(uvsAlternativeResponse['notAudible']); 1369 uvsAudioPlayer.play(); 1370 1371 widget_ignore_onend = true; 1372 uvsWidgetSearch.placeholder = uvsMessages['unableToHear']; 1373 } 1374 1375 if (event.error == 'audio-capture') { 1376 uvsAudioPlayer.configure(uvsAlternativeResponse['micConnect']); 1377 uvsAudioPlayer.play(); 1378 1379 widget_ignore_onend = true; 1380 uvsWidgetSearch.placeholder = uvsMessages['micNotAccessible']; 1381 } 1382 1383 if (event.error == 'not-allowed') { 1384 uvsAudioPlayer.configure(uvsAlternativeResponse['micConnect']); 1385 uvsAudioPlayer.play(); 1386 1387 widget_ignore_onend = true; 1388 uvsWidgetSearch.placeholder = uvsMessages['browserDenyMicAccess']; 1389 } 1390 }; 1391 1392 function widgetProcessEnd() { 1393 widgetRecognizing = false; 1394 1395 if (widget_ignore_onend) { return; } 1396 1397 widgetFinalTranscript = widget_final_transcript; 1398 uvsWidgetMic.classList.remove('listening'); 1399 1400 if (typeof (widgetFinalTranscript) != 'undefined' && widgetFinalTranscript.length != 0) { 1401 uvsWidgetSearch.value = widgetFinalTranscript; 1402 1403 // Play 'basic' playback 1404 uvsAudioPlayer.configure(uvsAlternativeResponse['basic'], function () { 1405 }); 1406 uvsAudioPlayer.play(); 1407 setTimeout(() => { 1408 uvsWidgetForm.submit(); 1409 }, 2000); 1410 } else { 1411 // Play 'notAudible' playback 1412 uvsAudioPlayer.configure(uvsAlternativeResponse['notAudible']); 1413 uvsAudioPlayer.play(); 1414 1415 uvsWidgetSearch.placeholder = uvsMessages['ask']; 1416 } 1417 1418 } 1419 1420 widgetRecognition.onend = function () { 1421 if (isAndroid) { widgetProcessEnd(); } 1422 }; 1423 1424 widgetRecognition.onresult = function (event) { 1425 let interim_transcript = ''; 1426 1427 if (typeof (event.results) == 'undefined') { 1428 widgetRecognition.onend = null; 1429 widgetRecognition.stop(); 1430 uvsWidgetSearch.placeholder = uvsMessages['unableToHear']; 1431 1432 // Play 'micConnect' playback 1433 uvsAudioPlayer.configure(uvsAlternativeResponse['micConnect']); 1434 uvsAudioPlayer.play(); 1435 1436 return; 1437 } 1438 1439 let eventResultsLength = event.results.length; 1440 1441 for (let i = event.resultIndex; i < eventResultsLength; ++i) { 1442 if (event.results[i].isFinal) { 1443 widget_final_transcript = event.results[i][0].transcript; 1444 1445 if (isAndroid == false) { 1446 widgetProcessEnd(); 1447 widgetRecognition.stop(); 1448 } 1449 } else { 1450 interim_transcript += event.results[i][0].transcript; 1451 } 1452 } 1453 }; 1454 1455 uvsWidgetMic.addEventListener(uvsMicEventToListen, function (event) { 1456 if (uvsAnyOtherMicListening(uvsWidgetMic.getAttribute('id'), uvsWidgetMic) === true) return; 1457 1458 if (widgetRecognizing) { 1459 // To clear pre-existing mic reset timeout if any. (Based on duration from settings) 1460 uvsClearMicResetTimeout(); 1461 1462 // Stop ongoing playback if nay 1463 if (uvsAudioPlayer.isPlaying()) { 1464 uvsAudioPlayer.stop(); 1465 } 1466 1467 if (isAndroid == false) { 1468 widgetProcessEnd(); 1469 widgetRecognition.stop(); 1470 } 1471 } else { 1472 uvsWidgetMic.classList.add('listening'); 1473 event.preventDefault(); 1474 1475 // Stop ongoing playback if nay 1476 if (uvsAudioPlayer.isPlaying()) { 1477 uvsAudioPlayer.stop(); 1478 } 1479 1480 widgetFinalTranscript = ''; 1481 widgetRecognizing = true; 1482 widgetRecognition.lang = !!uvsSttLanguageContext['gcp']['stt'] ? uvsSttLanguageContext['gcp']['langCode'] : 'en-US'; 1483 widgetRecognition.start(); 1484 widget_ignore_onend = false;
1485 1486 // To clear pre-existing mic reset timeout if any. (Based on duration from settings) 1487 uvsClearMicResetTimeout(); 1488 1489 // To set new mic reset timeout. (Based on duration from settings) 1490 window.uvsMicTimeoutIdentifier = setTimeout(function () { 1491 let updatedClassList = uvsWidgetMic.classList; 1492 1493 if (updatedClassList && updatedClassList.contains('listening')) { 1494 uvsWidgetMic.click(); 1495 } 1496 }, uvsMicListenAutoTimeoutDuration); 1497 } 1498 }); 1499 } else { 1500 //CODE FOR BROWSERS THAT DO NOT SUPPORT STT NATIVLY 1501 // MUST USE THE BUILT IN MICROPHONE 1502 uvsWidgetMic.addEventListener(uvsMicEventToListen, function (event) { 1503 /** 1504 * Audio element's play method must be invoked in exact influence of user gesture to avoid auto play restriction 1505 * 1506 */ 1507 if ( 1508 uvsClientInfo.ios === true 1509 || (uvsClientInfo.iosSafari && !uvsClientInfo.chrome && !uvsClientInfo.firefox && !uvsClientInfo.edge) 1510 || (uvsClientInfo.windows && uvsClientInfo.firefox) 1511 ) { 1512 uvsAudioPlayer.configure(uvsSilenceSoundPath); 1513 uvsAudioPlayer.play(); 1514 } 1515 1516 if (uvsAnyOtherMicListening(uvsWidgetMic.getAttribute('id'), uvsWidgetMic) === true) return; 1517 1518 // Deny recording if microphone is not accessible 1519 if (!uvsAudioRecorder || !uvsAudioContext) { 1520 uvsInitAudio(function (a) { 1521 if (!uvsAudioRecorder || !uvsAudioContext) { 1522 uvsWidgetSearch.placeholder = uvsMessages['micNotAccessible']; 1523 return false; 1524 } else { 1525 widgetListenEvent(); 1526 } 1527 }); 1528 } else { 1529 widgetListenEvent(); 1530 } 1531 1532 function widgetListenEvent() { 1533 // If API system key is unavailable then acknowledge service unavailability and stop voice navigation. 1534 if (!(typeof (vs.uvsXApiKey) != 'undefined' && vs.uvsXApiKey !== null)) { 1535 // Play 'unavailable' playback 1536 uvsAudioPlayer.configure(uvsAlternativeResponse['unavailable']); 1537 uvsAudioPlayer.play(); 1538 1539 return false; 1540 } 1541 1542 // User ending recording by clicking back mic 1543 if (widgetRecognizing) { 1544 // To clear pre-existing mic reset timeout if any. (Based on duration from settings) 1545 uvsClearMicResetTimeout(); 1546 1547 // Stop recorder 1548 uvsAudioRecorder.stop(); 1549 1550 // Stop access to audio resource 1551 uvsStopAudio(); 1552 1553 // Stop ongoing playback if nay 1554 if (uvsAudioPlayer.isPlaying()) { 1555 uvsAudioPlayer.stop(); 1556 } 1557 1558 //replace recording with mic icon 1559 uvsWidgetMic.classList.remove('listening'); 1560 1561 uvsWidgetSearch.placeholder = uvsMessages['transcribeText']; 1562 1563 uvsAudioRecorder.getBuffers(function (buffers) { 1564 if (!!uvsSttLanguageContext['gcp']['stt']) { 1565 uvsAudioRecorder.exportMonoWAV(function (blob) { 1566 uvsAudioRecorder.convertBlobToBase64(blob).then(function (resultedBase64) { 1567 uvsGcpStt(resultedBase64).then(function (transcriptResult) { 1568 uvsWidgetSearch.value = transcriptResult; 1569 1570 // Play 'basic' playback 1571 uvsAudioPlayer.configure(uvsAlternativeResponse['basic'], function () { 1572 }); 1573 uvsAudioPlayer.play(); 1574 setTimeout(() => { 1575 uvsWidgetForm.submit(); 1576 }, 2000); 1577 }).catch(function (error) { 1578 // Play 'notAudible' playback 1579 uvsAudioPlayer.configure(uvsAlternativeResponse['notAudible']); 1580 uvsAudioPlayer.play(); 1581 1582 uvsWidgetSearch.placeholder = uvsMessages['ask']; 1583 }) 1584 }).catch(function (error) { 1585 // Play 'notAudible' playback 1586 uvsAudioPlayer.configure(uvsAlternativeResponse['notAudible']); 1587 uvsAudioPlayer.play(); 1588 1589 uvsWidgetSearch.placeholder = uvsMessages['ask']; 1590 }); 1591 }); 1592 } else { 1593 // Play 'notAudible' playback 1594 uvsAudioPlayer.configure(uvsAlternativeResponse['notAudible']); 1595 uvsAudioPlayer.play(); 1596 1597 uvsWidgetSearch.placeholder = uvsMessages['ask']; 1598 } 1599 }); 1600 1601 widgetRecognizing = false;
1602 return; 1603 } else {// User started recording by clicking mic 1604 uvsWidgetMic.classList.add('listening'); 1605 event.preventDefault(); 1606 1607 // Stop ongoing playback if nay 1608 if (uvsAudioPlayer.isPlaying()) { 1609 uvsAudioPlayer.stop(); 1610 } 1611 1612 widgetFinalTranscript = ''; 1613 1614 widgetRecognizing = true; 1615 uvsAudioRecorder.clear(); 1616 uvsAudioRecorder.record(uvsWidgetMic); 1617 1618 // To clear pre-existing mic reset timeout if any. (Based on duration from settings) 1619 uvsClearMicResetTimeout(); 1620 1621 // To set new mic reset timeout. (Based on duration from settings) 1622 window.uvsMicTimeoutIdentifier = setTimeout(function () { 1623 let updatedClassList = uvsWidgetMic.classList; 1624 1625 if (updatedClassList && updatedClassList.contains('listening')) { 1626 uvsWidgetMic.click(); 1627 } 1628 }, uvsMicListenAutoTimeoutDuration); 1629 } 1630 } 1631 }, false); 1632 } 1633 } 1634 1635 // Elementor 1636 if (uvsIsElementor == true) { 1637 enableElementor(); 1638 } 1639 // Singleclick 1640 if (uvsIsSingleClick == true) { 1641 enableSingleClick(); 1642 uvsFloatingMicKeyBoardAccess('single'); 1643 } 1644 // disable single click 1645 else { 1646 disableSingleClick(); 1647 uvsFloatingMicKeyBoardAccess('double'); 1648 } 1649 1650 1651 /*###############################################################################*/ 1652 1653 /** 1654 * Function to toggle class of the HTML element 1655 * 1656 * @param {elmSelector - String} : CSS Selector 1657 * @param {nameOfClass - String} : Class name to add/remove 1658 */ 1659 function uvsToggleClass(elmSelector, nameOfClass) { 1660 if (!(typeof (elmSelector) != 'undefined' && elmSelector != null && elmSelector.length != 0)) return false; 1661 1662 let element = document.querySelector(elmSelector); 1663 1664 if (element.classList) { 1665 element.classList.toggle(nameOfClass); 1666 } else { 1667 // For IE9 1668 1669 let classes = element.className.split(" "); 1670 let i = classes.indexOf(nameOfClass); 1671 1672 if (i >= 0) { 1673 classes.splice(i, 1); 1674 } else { 1675 classes.push(nameOfClass); 1676 element.className = classes.join(" "); 1677 } 1678 } 1679 } 1680 1681 /** 1682 * Function to toggle chat and links 1683 */ 1684 function uvsToggleWidgetElements() { 1685 if (uvsIsSingleClick == true) { 1686 uvsToggleClass('.uvs-widget-toggle-button', 'uvs-toggle-btn-mic'); 1687 uvsToggleClass('.uvs-widget-toggle-button', 'uvs-toggle-btn-mic'); 1688 uvsToggleClass('.uvs-widget-toggle-button', 'uvs-widget-active'); 1689 uvsToggleClass('.uvs-widget-toggle-button', 'uvs-widget-visible'); 1690 uvsToggleClass('#uvsWidgetToggleButton', 'uvs-widget-float'); 1691 uvsToggleClass('.uvs-widget-button', 'uvs-widget-visible'); 1692 } 1693 else { 1694 uvsToggleClass('.uvs-widget-toggle-button', 'uvs-toggle-btn-mic'); 1695 uvsToggleClass('.uvs-widget-toggle-button', 'uvs-toggle-btn-close'); 1696 uvsToggleClass('.uvs-widget-toggle-button', 'uvs-widget-active'); 1697 uvsToggleClass('.uvs-widget-toggle-button', 'uvs-widget-visible'); 1698 uvsToggleClass('#uvsWidgetToggleButton', 'uvs-widget-float'); 1699 uvsToggleClass('.uvs-widget-chat-wrapper', 'uvs-widget-visible'); 1700 uvsToggleClass('.uvs-widget-button', 'uvs-widget-visible'); 1701 } 1702 1703 } 1704 1705 })(); 1706};
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.