1"use strict"; 2 3jQuery( document ).ready( function( $ ) { 4 5 //console.log('wroking') 6 7 let gumStream; // Stream from getUserMedia() 8 let rec; // Recorder.js object 9 let recorder; // Recorder.js object 10 let input; // MediaStreamAudioSourceNode we'll be recording 11 let audioContext; 12 let audioBlob; 13 let drawVisual; 14 let draw; 15 let timerInterval; 16 let countdownInterval; 17 let isTimerPaused = false; 18 let isCountdownPaused = false; 19 let form; 20 let sampleRate; 21 let encodeAfterRecord = true; 22 23 var record_object = {}; 24 record_object.record_id = []; 25 record_object.type = 'qccf7wpvoicemessage_gravityforms'; 26 27 /** Open popup. */ 28 let $fbutton = $('#vmwpmdp-wpvoicemessage_gravityforms-fbutton'); 29 if ($fbutton.length) { 30 $fbutton.on('click', openPopup); 31 } 32 33 /** Close modal by overlay click. */ 34 let $overlay = $('.vmwpmdp-wpvoicemessage_gravityforms-fbutton-overlay'); 35 if ($overlay.length) { 36 $overlay.on('click', closePopup); 37 38 /** Close modal by close button click. */ 39 $overlay.find('.vmwpmdp-wpvoicemessage_gravityforms-fbutton-modal .vmwpmdp-wpvoicemessage_gravityforms-close').on('click', closePopup); 40 } 41 42 /** 43 * Do not close popup by click on window 44 * and stop event propagation to overlay. 45 **/ 46 let $popup = $('.vmwpmdp-wpvoicemessage_gravityforms-fbutton-modal'); 47 if ($popup.length) { 48 $popup.on('click', (event) => { event.stopPropagation(); }); 49 } 50 51 /** Add Events to buttons using event delegation for dynamic content. */ 52 // Attaching a listener to a static parent (.vmwpmdp-wpvoicemessage_gravityforms-form-box) 53 // or document.body is much more robust than looping through elements, 54 // especially if you are loading them via AJAX. 55 $(document).on('click', '.vmwpmdp-wpvoicemessage_gravityforms-form-box .vmwpmdp-wpvoicemessage_gravityforms-start-btn', function(e) { 56 const $cForm = $(this).closest('.vmwpmdp-wpvoicemessage_gravityforms-form-box'); 57 const parent_id = $(this).closest('[id]').attr('id'); 58 startRecordingButtonClick($cForm, e, parent_id); 59 }); 60 61 $(document).on('click', '.vmwpmdp-wpvoicemessage_gravityforms-form-box .vmwpmdp-reset-rec-btn', function() { 62 const $cForm = $(this).closest('.vmwpmdp-wpvoicemessage_gravityforms-form-box'); 63 if ($(this).closest('.vmwpmdp-speak-now-btns').length) { 64 resetButtonClick($cForm); 65 } else if ($(this).closest('.vmwpmdp-send-btns').length) { 66 resetSendButtonClick($cForm); 67 } 68 }); 69 70 $(document).on('click', '.vmwpmdp-wpvoicemessage_gravityforms-form-box .vmwpmdp-stop-rec-btn', function(e) { 71 const $cForm = $(this).closest('.vmwpmdp-wpvoicemessage_gravityforms-form-box'); 72 const parent_id = $(this).closest('[id]').attr('id'); 73 stopButtonClick($cForm, e, parent_id); 74 }); 75 76 $(document).on('click', '.vmwpmdp-wpvoicemessage_gravityforms-thanks-box .vmwpmdp-restart', function() { 77 const $cForm = $(this).closest('.vmwpmdp-wpvoicemessage_gravityforms-form-box'); 78 yesResetButtonClick($cForm); 79 }); 80 81 $(document).on('click', '.vmwpmdp-wpvoicemessage_gravityforms-send-box .vmwpmdp-send-rec-btn', function() { 82 const $cForm = $(this).closest('.vmwpmdp-wpvoicemessage_gravityforms-form-box'); 83 sendButtonClick($cForm); 84 }); 85 86 $(document).on('click', '.vmwpmdp-wpvoicemessage_gravityforms-form-box .vmwpmdp-reset-rec-back', function() { 87 const $cForm = $(this).closest('.vmwpmdp-wpvoicemessage_gravityforms-form-box'); 88 backButtonClick($cForm); 89 }); 90 91 $(document).on('click', '.vmwpmdp-wpvoicemessage_gravityforms-form-box .vmwpmdp-reset-rec-yes', function() { 92 const $cForm = $(this).closest('.vmwpmdp-wpvoicemessage_gravityforms-form-box'); 93 yesResetButtonClick($cForm); 94 }); 95 96 $(document).on('click', '.vmwpmdp-wpvoicemessage_gravityforms-form-box .vmwpmdp-reset-rec-no', function(e) { 97 const $cForm = $(this).closest('.vmwpmdp-wpvoicemessage_gravityforms-form-box'); 98 noResetButtonClick($(e.target), $cForm); 99 });
100 101 // Handle form submission using AJAX. 102 $('.vmwpmdp-wpvoicemessage_gravityforms-send-box form').on('submit', function(e) { 103 e.preventDefault(); 104 }); 105 106 /** Try again button after microphone access error. */ 107 $('.vmwpmdp-mic-access-err-reload-btn').on('click', () => { location.reload(); }); 108 109 110 /** 111 * Open Popup. 112 **/ 113 function openPopup() { 114 let $root = $('html'); 115 let $overlay = $('.vmwpmdp-wpvoicemessage_gravityforms-fbutton-overlay'); 116 $overlay.show(); 117 $root.addClass('vmwpmdp-wpvoicemessage_gravityforms-modal-opened'); 118 } 119 120 /** 121 * Close Popup. 122 **/ 123 function closePopup() { 124 let $root = $('html'); 125 let $overlay = $('.vmwpmdp-wpvoicemessage_gravityforms-fbutton-overlay'); 126 127 /** Reverse animation */ 128 $root.addClass('vmwpmdp-wpvoicemessage_gravityforms-reverse'); 129 130 /** Hide all */ 131 setTimeout(function() { 132 $overlay.hide(); 133 $root.removeClass('vmwpmdp-wpvoicemessage_gravityforms-modal-opened'); 134 $root.removeClass('vmwpmdp-wpvoicemessage_gravityforms-reverse'); 135 136 /** Show Start Recording step. */ 137 let $pop_cForm = $('.vmwpmdp-wpvoicemessage_gravityforms-fbutton-modal .vmwpmdp-wpvoicemessage_gravityforms-form-box'); 138 showStartRecordingStep($pop_cForm); 139 140 /** Stop all media stream. */ 141 stopAllStreams(); 142 143 }, 600); 144 } 145 146 /** 147 * No button on Reset view. 148 **/ 149 function noResetButtonClick($target, $cForm) { 150 if ('showSendStep' === $target.attr('show-step')) { 151 /** Show Send view. */ 152 showSendStep($cForm); 153 $target.attr('show-step', ''); 154 } else { 155 /** Resume Recording. */ 156 rec.record(); 157 log('Recording resumed.'); 158 159 /** Resume timer. */ 160 isTimerPaused = false; 161 162 /** Resume Countdown. */ 163 isCountdownPaused = false; 164 165 /** Resume animation. */ 166 drawVisual = window.requestAnimationFrame(draw); 167 168 /** Show Speak Now view. */ 169 showSpeakNowStep($cForm); 170 } 171 } 172 173 174 /** 175 * Handles the click of the "Send" button from the "Send" view. 176 */ 177 function sendButtonClick($cForm) { 178 // console.log('Send button clicked for form:', $cForm); 179 180 fillCurrentURL($cForm); 181 fillUserDetails($cForm); 182 183 // Show animation 184 $cForm.addClass('vmwpmdp-busy'); 185 186 // Get additional fields form 187 const $additionalForm = $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-send-box form'); 188 189 let formData; 190 191 if ($additionalForm.length) { 192 // Validate additional fields 193 if (!$additionalForm.get(0).checkValidity()) { 194 $additionalForm.get(0).reportValidity(); 195 $cForm.removeClass('vmwpmdp-busy'); 196 return; 197 } 198 // Get form data from the form 199 formData = new FormData($additionalForm.get(0)); 200 } else { 201 // Create a new FormData object if no form exists 202 formData = new FormData(); 203 } 204 205 // Add audio data 206 formData.append('vmwpmdp-wpvoicemessage_gravityforms-audio', audioBlob); 207 208 // Add audio Sample Rate 209 formData.append('vmwpmdp-wpvoicemessage_gravityforms-audio-sample-rate', sampleRate); 210 211 // Add Action to process on WP side 212 formData.append('action', 'wpvoicemessage_gravityforms_send'); 213 214 // Add nonce to security 215 formData.append('nonce', vmwpmdpContacterWP.nonce); 216 217 // Add cform id 218 formData.append('cform-id', $cForm.attr('cform-id')); 219 220 // Send data to WP using jQuery's $.ajax 221 $.ajax({
222 url: vmwpmdpContacterWP.ajaxurl, 223 type: 'POST', 224 data: formData, 225 processData: false, // Prevents jQuery from trying to convert the FormData object 226 contentType: false, // Prevents jQuery from setting a default content type 227 success: function(response) { 228 // Hide animation 229 $cForm.removeClass('vmwpmdp-busy'); 230 231 // Check for specific Gravityforms wrapper classes 232 if ($cForm.closest('.gform_body').length || $cForm.hasClass('vmwpmdp-wpvoicemessage_gravityforms-form-box')) { 233 234 try { 235 const responseJson = response; 236 //console.log(response) 237 if (responseJson.status == 'ok' && responseJson.post_id > 0) { 238 239 const $urlField = $cForm.find('.clr-wpvoicemessage_gravityforms-field').first(); 240 if ($urlField.length) { 241 const records_ids = record_object.record_id; 242 records_ids.pop(); 243 records_ids.push(responseJson.post_id); 244 record_object.type = 'clrwpvoicemessage_gravityforms'; 245 $urlField.val(responseJson.audio_file_path); 246 247 var record_input_val = $cForm.closest('.ginput_complex').find('.qcld_audio_record_input input').first(); 248 if (record_input_val.length === 0) { 249 record_input_val = $cForm.closest('.gfield').find('.qcld_audio_record_input input').first(); 250 } 251 if (record_input_val.length === 0) { 252 record_input_val = $cForm.closest('.gform_body').find('.qcld_audio_record_input input').first(); 253 } 254 record_input_val.val( responseJson.audio_file_path ); 255 } 256 showThanksStep($cForm); 257 } else { 258 // Show error message 259 showErrorStep($cForm); 260 } 261 } catch (e) { 262 //console.error('Failed to parse JSON response:', e); 263 showErrorStep($cForm); 264 } 265 } else { 266 showThanksStep($cForm); 267 } 268 }, 269 error: function(jqXHR, textStatus, errorThrown) { 270 //console.error('AJAX request failed:', textStatus, errorThrown); 271 $cForm.removeClass('vmwpmdp-busy'); 272 showErrorStep($cForm); 273 } 274 }); 275 } 276 277 278 // Helper functions (replace with your actual implementation) 279 function startRecordingButtonClick($cForm, e, parent_id) { 280 //console.log('startRecordingButtonClick triggered on', $cForm, 'with event', e, 'and parent ID', parent_id); 281 282 // Using browser API to get microphone access 283 navigator.mediaDevices.getUserMedia({ audio: true }) 284 .then(function(stream) { 285 //console.log('getUserMedia() success, stream created, beginning recording'); 286 287 // Store the stream globally (or pass it along) 288 gumStream = stream; 289 290 // Use the stream 291 audioContext = new AudioContext(); 292 input = audioContext.createMediaStreamSource(stream); 293 294 // Create the Recorder.js object 295 // var rec = new Recorder(input, { 296 // numChannels: 1 297 // }); 298 299 // // Start the recording 300 // rec.record(); 301 // console.log('Recording started'); 302 303 var numChannels = vmwpmdpContacterWP.convert_recorded_audio == 'mp3' ? 2 : 1; 304 305 rec = new WebAudioRecorder(input, { 306 workerDir: vmwpmdpContacterWP.QC_VOICEWIDGET_ASSETS_URL, // must end with slash 307 encoding: vmwpmdpContacterWP.convert_recorded_audio, 308 numChannels:numChannels, //2 is the default, mp3 encoding supports only 2 309 onEncoderLoading: function(recorder, encoding) { 310 // show "loading encoder..." display 311 // __log("Loading "+encoding+" encoder..."); 312 }, 313 onEncoderLoaded: function(recorder, encoding) { 314 // hide "loading encoder..." display 315 //__log(encoding+" encoder loaded"); 316 } 317 }); 318 319 rec.onComplete = function(recorder, blob) { 320 //__log("Encoding complete"); 321 // createDownloadLink(blob,recorder.encoding); 322 // createPayer( blob, cForm ) 323 createPayer( blob, $cForm ) 324 // encodingTypeSelect.disabled = false; 325 } 326 327 let maxDurations = parseInt( $cForm.attr( 'max-duration' ) ); 328 329 //console.log( e ); 330 331 rec.setOptions({ 332 timeLimit: maxDurations, 333 encodeAfterRecord:encodeAfterRecord, 334 ogg: {quality: 1}, 335 mp3: {bitRate: parseInt(vmwpmdpContacterWP.voice_bit_rate)} 336 }); 337 338 // start the recording process 339 rec.startRecording(); 340 341 // --- UI Updates with jQuery --- 342 // Hide the initial start box 343 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-start-btn-box').hide(); 344 // Show the "speak now" box 345 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-recording-box').show(); 346 347 // Start any visual timers or animations 348 startTimer($cForm); 349 startVisuals($cForm); 350 351 }) 352 .catch(function(err) { 353 //console.error('Recording failed:', err); 354 // --- UI Updates with jQuery on failure --- 355 // Hide the current step 356 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-start-btn-box').hide(); 357 // Show the microphone access error box 358 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-mic-access-err-box').show(); 359 }); 360 } 361 362 363 /** 364 * Creates an audio player and visual waveform display for a recorded audio blob. 365 * 366 * @param {Blob} blob The recorded audio blob. 367 * @param {jQuery} $cForm The jQuery-wrapped form element. 368 */ 369 function createPayer(blob, $cForm) { 370 const url = URL.createObjectURL(blob); 371 audioBlob = blob; 372 373 // Create the audio element with jQuery 374 const $audioEl = $('<audio controls></audio>').attr('src', url); 375 376 // Add audio element to the player box, first clearing old content 377 const $playerBox = $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-send-box .vmwpmdp-wpvoicemessage_gravityforms-player-box'); 378 //$playerBox.empty().append($audioEl); 379 $playerBox.empty(); 380 381 // Set Download Link if it exists 382 const $downloadBtn = $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-download-l
382ink-box > a'); 383 if ($downloadBtn.length) { 384 $downloadBtn.attr({ 385 'href': url, 386 'download': 'record.wav' 387 }); 388 } 389 390 // Create the play button 391 const $playButton = $('<a>') 392 .html('<span class="dashicons dashicons-controls-play"></span>') 393 .addClass('qc_playpause_wavesurfer'); 394 395 const $bodyPlayButton = $cForm.find('.waveform'); 396 const containerElement = $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-player-box').get(0); // Native element for Wavesurfer 397 398 // Clear and append elements 399 $bodyPlayButton.empty().append($playButton).append($playerBox); 400 401 let wavesurfer; 402 if ((typeof wpvoicevar !== "undefined") && (wpvoicevar.wave_style === 'bar-style')) { 403 wavesurfer = WaveSurfer.create({ 404 container: containerElement, 405 waveColor: '#38ada9', 406 progressColor: '#079992', 407 barWidth: 2, 408 barHeight: 1, 409 barGap: null, 410 height: 50, 411 responsive: true, 412 normalize: true, 413 scrollParent: true, 414 hideScrollbar: true 415 }); 416 } else { 417 wavesurfer = WaveSurfer.create({ 418 container: containerElement, 419 waveColor: '#38ada9', 420 progressColor: '#079992', 421 barGap: null, 422 height: 50, 423 responsive: true, 424 normalize: true, 425 scrollParent: true, 426 hideScrollbar: true 427 }); 428 } 429 430 wavesurfer.load(url); 431 432 // Attach event listener using jQuery's .on() 433 $playButton.on("click", function() { 434 wavesurfer.on('pause', function() { 435 $playButton.html('<span class="dashicons dashicons-controls-play"></span>'); 436 }); 437 wavesurfer.on('play', function() { 438 $playButton.html('<span class="dashicons dashicons-controls-pause"></span>'); 439 }); 440 wavesurfer.playPause(); 441 }); 442 } 443 444 445 // Placeholder functions that you would also need to define 446 // A new global variable to store the timer's current time in seconds 447 let timerSeconds = 0; 448 // The timer display element 449 let $timerDisplay; 450 451 /** 452 * Starts the recording timer. 453 */ 454 function startTimer($cForm) { 455 // Check if the timer is already running 456 if (timerInterval) { 457 //console.log('Timer is already running.'); 458 return; 459 } 460 461 // Find the timer display element within the active form 462 $timerDisplay = $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-recording-countdown'); 463 464 // Make sure we have a timer display element 465 if (!$timerDisplay.length) { 466 //console.error('Timer display element not found.'); 467 return; 468 } 469 470 let maxDuration = parseInt( $cForm.attr( 'max-duration' ) ); 471 472 if ( maxDuration === 0 ) { return; } // If maxDuration set to unlimited, countdown is impossible. 473 474 let countdown = maxDuration; 475 476 isTimerPaused = false; 477 478 // Use setInterval to update the timer every second 479 timerInterval = setInterval(() => { 480 if (!isTimerPaused) { 481 timerSeconds++; 482 const minutes = Math.floor(timerSeconds / 60); 483 const seconds = timerSeconds % 60; 484 485 countdown--; 486 /** If timer lower than 0 Stop recording. */ 487 if ( maxDuration !== 0 && countdown < 0 ) { 488 $cForm.find( '.vmwpmdp-speak-now-btns .vmwpmdp-stop-rec-btn' ).click(); 489 } 490 491 // Format the time with leading zeros 492 const formattedMinutes = minutes < 10 ? '0' + minutes : minutes; 493 const formattedSeconds = seconds < 10 ? '0' + seconds : seconds; 494 495 // Update the display 496 $timerDisplay.text(`${formattedMinutes}:${formattedSeconds}`); 497 } 498 }, 1000); 499 } 500 501 /** 502 * Pauses the timer without resetting. 503 */ 504 function pauseTimer() { 505 isTimerPaused = true; 506 //console.log('Timer paused.'); 507 } 508 509 /** 510 * Stops and resets the timer. 511 */ 512 function stopTimer() { 513 clearInterval(timerInterval); 514 timerInterval = null; 515 timerSeconds = 0; 516 isTimerPaused = false; 517 if ($timerDisplay) { 518 $timerDisplay.text('00:00'); // Reset display 519 } 520 //console.log('Timer stopped and reset.'); 521 } 522 523 524 // Global variable to hold the AnalyserNode and data array 525 let analyser; 526 let dataArray;
527 let canvasContext; 528 let canvas; 529 530 // Remove global canvas and canvasContext variables from the top. 531 // The context will now be accessed from the form's data. 532 533 /** 534 * Starts the visual feedback for the audio recording. 535 */ 536 function startVisuals($cForm) { 537 //console.log('Starting audio visualizer.'); 538 539 /** 540 * Create Analyser to extract data from audio source. 541 * The AnalyserNode interface represents a node able to provide real-time frequency and time-domain analysis information. 542 **/ 543 let analyser = audioContext.createAnalyser(); 544 545 /** Connect analyser to audio source. */ 546 input.connect( analyser ); 547 548 /** Array to receive the data from audio source. */ 549 analyser.fftSize = 2048; 550 let bufferLength = analyser.frequencyBinCount; 551 let dataArray = new Uint8Array( bufferLength ); 552 553 /** Canvas for animation. */ 554 let animation = $cForm.find( '.vmwpmdp-wpvoicemessage_gravityforms-recording-animation canvas' )[0]; 555 556 let animationCtx = animation.getContext( "2d" ); 557 558 /** Clear the canvas. */ 559 animationCtx.clearRect( 0, 0, animation.width, animation.height ); 560 561 draw = function() { 562 563 /** Using requestAnimationFrame() to keep looping the drawing function once it has been started. */ 564 drawVisual = requestAnimationFrame( draw ); 565 566 /** Grab the time domain data and copy it into our array. */ 567 analyser.getByteTimeDomainData( dataArray ); 568 569 /** Fill the canvas with a solid colour to start. */ 570 animationCtx.clearRect( 0, 0, animation.width, animation.height ); // Clear the canvas. 571 animationCtx.fillStyle = 'rgba( 255, 255, 255, 0.01 )'; // Almost transparent 572 animationCtx.fillRect( 0, 0, animation.width, animation.height ); 573 574 /** Set a line width and stroke colour for the wave we will draw, then begin drawing a path. */ 575 animationCtx.lineWidth = 2; 576 577 let startColor = vmwpmdpContacterWP.accentColor.replace(/[^,]+(?=\))/, '0'); 578 let endColor = vmwpmdpContacterWP.accentColor.replace(/[^,]+(?=\))/, '1'); 579 580 const gradient = animationCtx.createLinearGradient(0, 0, 384, 0); 581 gradient.addColorStop( 0, startColor ); 582 gradient.addColorStop( .25 , endColor ); 583 gradient.addColorStop( .75 , endColor ); 584 gradient.addColorStop( 1, startColor ); 585 animationCtx.strokeStyle = gradient; 586 587 animationCtx.beginPath(); 588 589 /** 590 * Determine the width of each segment of the line to be drawn 591 * by dividing the canvas width by the array length (equal to the FrequencyBinCount, as defined earlier on), 592 * then define an x variable to define the position to move to for drawing each segment of the line. 593 **/ 594 let sliceWidth = animation.width * 1.0 / bufferLength; 595 let x = 0; 596 597 /** 598 * Run through a loop, defining the position of a small segment of the wave 599 * for each point in the buffer at a certain height based on the data point value form the array, 600 * then moving the line across to the place where the next wave segment should be drawn. 601 **/ 602 for ( let i = 0; i < bufferLength; i++ ) { 603 604 let v = dataArray[i] / 128.0; 605 let y = v * animation.height/2; 606 607 if ( i === 0 ) { 608 animationCtx.moveTo( x, y ); 609 } else { 610 animationCtx.lineTo( x, y ); 611 } 612 613 x += sliceWidth; 614 } 615 616 /** 617 * Finish the line in the middle of the right hand side of the canvas, 618 * then draw the stroke we've defined. 619 **/ 620 animationCtx.lineTo( animation.width, animation.height/2 ); 621 animationCtx.stroke(); 622 }; 623 624 /** Call the draw() function to start off the whole process. */ 625 draw(); 626 627 // Call the draw function with the form object 628 //draw($cForm); 629 } 630 631 632 633 634 /** 635 * Stops the visual feedback loop. 636 */ 637 function stopVisuals() { 638 if (drawVisual) { 639 cancelAnimationFrame(drawVisual); 640 } 641 // Clean up analyser and canvas context if necessary 642 if (analyser) { 643 analyser.disconnect(); 644 } 645 } 646 647 648 649 /** 650 * Yes button on Reset view. 651 **/ 652 function yesResetButtonClick($cForm) { 653 654 /** Stop the recording. */ 655 if (rec) { 656 rec.finishRecording(); 657 } 658 659 hideAllViews( $cForm ); 660 661 /** Stop microphone access. */ 662 if (gumStream) { 663 gumStream.getAudioTracks()[0].stop(); 664 } 665 666 /** Show Start Recording step. */ 667 showStartRecordingStep($cForm); 668 } 669 670 function resetButtonClick($cForm) { 671 // This function is likely triggered from the "Speak Now" view. 672 //console.log('Reset button clicked for form:', $cForm); 673 674 // Stop the recording (using a global variable or object) 675 if (rec) { 676 rec.finishRecording(); 677 //console.log('Recording stopped.'); 678 } 679 680 // Reset any UI timers or visuals 681 stopTimer(); 682 stopVisuals(); 683 684 // Show the initial "Start Recording" step 685 showStartRecordingStep($cForm); 686 687 // Potentially stop the media stream as well, if that's part of the reset flow 688 if (gumStream) { 689 gumStream.getAudioTracks()[0].stop(); 690 // console.log('Microphone access stopped.'); 691 } 692 693 // Since this is a "reset", you might also want to clear any recording-related data 694 audioBlob = null; 695 rec = null; 696 697 // You could also manually reset form fields if needed 698 // $cForm.find('input, textarea, select').val(''); 699 // 700 showSpeakNowStep($cForm) 701 } 702 703 704 /** 705 * Handles the "Stop" button click. 706 */ 707 function stopButtonClick($cForm, e, parent_id) { 708 // console.log('stopButtonClick triggered for form:', $cForm); 709 710 // Defensive check: Ensure the recorder object exists before trying to use it. 711 if (!rec) { 712 //console.warn('Recorder object not available. Was recording started correctly?'); 713 714 // Handle the UI gracefully, e.g., show an error message or just hide the controls. 715 // For example, if recording failed, you might show the mic access error box again. 716 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-speak-now-box').hide(); 717 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-mic-access-err-box').show(); 718 719 // Stop any active streams, just in case 720 stopAllStreams(); 721 722 return; 723 } 724 725 // Stop the recording 726 rec.finishRecording(); 727 //console.log('Recording stopped.'); 728 729 // Stop all media streams 730 stopAllStreams(); 731 732 // Stop timers and visuals 733 stopTimer(); 734 stopVisuals(); 735 736 //createPayer(blob, $cForm); 737 738 // --- UI Updates with jQuery --- 739 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-recording-box').hide(); 740 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-send-box').show(); 741 742 } 743 744 745 /** 746 * Handles the click of the "Reset" button from the "Send" view. 747 */ 748 function resetSendButtonClick($cForm) { 749 //console.log('Reset button clicked from the send view for form:', $cForm); 750 751 // 1. Clear any stored recording data 752 audioBlob = null; 753 rec = null; 754 755 // 2. Stop any media stream that might still be active (unlikely, but safe) 756 if (gumStream) { 757 gumStream.getAudioTracks().stop(); 758 // console.log('Microphone access stopped.'); 759 } 760 761 // 3. Reset any UI timers or visuals (if they were still running) 762 stopTimer(); 763 stopVisuals(); 764 765 // 4. Remove the dynamically created audio player 766 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-send-box .vmwpmdp-wpvoicemessage_gravityforms-player-box').empty(); 767 768 // 5. Reset the form's input fields to their default state 769 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-send-box .clr-wpvoicemessage_gravityforms-field').val(''); 770 771 // 6. Show the initial "Start Recording" step 772 showStartRecordingStep($cForm); 773 774 //console.log('Form fully reset. Ready for a new recording.'); 775 } 776 777 /** 778 * Resets and shows the initial "Start Recording" step for a specific form. 779 */ 780 function showStartRecordingStep($cForm) { 781 //console.log('Showing the start recording step for form:', $cForm); 782 783 // Hide all other views within the form 784 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-recording-box').hide(); 785 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-send-box').hide(); 786 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-thanks-box').hide(); 787 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-error-box').hide(); 788 789 // Show the "Start Recording" view 790 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-start-btn-box').show(); 791 792 // Optional: Hide any busy or error animations 793 $cForm.removeClass('vmwpmdp-busy'); 794 795 // Optional: Clear any previous data or timers for a clean slate 796 stopTimer(); 797 stopVisuals(); 798 audioBlob = null; 799 rec = null; 800 } 801 802 /** 803 * Stops all tracks on the active media stream. 804 */ 805 function stopAllStreams() { 806 // Check if the stream object exists 807 if (gumStream) { 808 // Get all tracks from the stream 809 const tracks = gumStream.getTracks(); 810 811 // Iterate over all tracks and stop each one individually 812 tracks.forEach(track => { 813 track.stop(); 814 //console.log('Stopped media track:', track.kind); 815 }); 816 817 // Optional: Disconnect the input from the audio context 818 if (input) { 819 input.disconnect(); 820 } 821 822 // Reset the global stream variable 823 gumStream = null; 824 //console.log('All media streams stopped and resources released.'); 825 } else { 826 //console.log('No active media streams to stop.'); 827 } 828 } 829 /** 830 * Shows the "Send" step and hides all other views for a specific form. 831 */ 832 function showSendStep($cForm) { 833 //console.log('Showing the send step for form:', $cForm); 834 835 // Hide all other views within the form 836 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-start-btn-box').hide(); 837 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-recording-box').hide(); 838 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-thanks-box').hide(); 839 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-error-box').hide(); 840 841 // Show the "Send" view 842 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-send-box').show(); 843 844 845 // Optional: Hide any busy or error animations 846 $cForm.removeClass('vmwpmdp-busy'); 847 } 848 849 850 const LOG_LEVEL = 'debug'; // Change this to 'info', 'warn', etc. 851 const LOG_LEVELS = { 852 'debug': 0, 853 'info': 1, 854 'warn': 2, 855 'error': 3 856 }; 857 858 function log(level, message) { 859 if (LOG_LEVELS[level] >= LOG_LEVELS[LOG_LEVEL]) { 860 const timestamp = new Date().toISOString(); 861 console.log(`[${timestamp}] [${level.toUpperCase()}]`, message); 862 } 863 } 864 865 /** 866 * Shows the "Speak Now" step and hides other views for a specific form. 867 */ 868 function showSpeakNowStep($cForm) { 869 //console.log('Showing the "Speak Now" step for form:', $cForm); 870 871 // Hide other views 872 hideAllViews( $cForm ); 873 874 // Show the "Speak Now" view 875 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-recording-box').show(); 876 877 // Optional: Hide any busy or error animations 878 $cForm.removeClass('vmwpmdp-busy'); 879 } 880 /** 881 * Fills a form input with the current page's URL. 882 */ 883 function fillCurrentURL($cForm) { 884 // 1. Get the current page's full URL 885 const currentUrl = window.location.href; 886 //console.log('Current URL is:', currentUrl); 887 888 // 2. Find the input field within the specific form and set its value 889 // Assuming you have a hidden input field for this purpose, like: 890 // <input type="hidden" name="page_url" class="vmwpmdp-page-url"> 891 const $urlInput = $cForm.find('#vmwpmdpCurrentURL'); 892 893 if ($urlInput.length) { 894 $urlInput.val(currentUrl); 895 //console.log('Form field for URL populated.'); 896 } else { 897 // console.warn('URL input field not found. Skipping population.'); 898 } 899 } 900 901 /** 902 * Populates form fields with user details retrieved from the server.
903 */ 904 function fillUserDetails($cForm) { 905 //console.log('Attempting to fill user details for form:', $cForm); 906 907 // Assume an endpoint exists to fetch user data. In WordPress, 908 // this would be a custom REST API endpoint or an AJAX action. 909 //const userApiEndpoint = 'your-wordpress-api-endpoint/get-user-data'; 910 911 let vmwpmdpUserDetails = $cForm.find( '#vmwpmdpUserDetails' )[0]; 912 if ( 'undefined' === typeof vmwpmdpUserDetails ) { return; } 913 914 let login = $cForm.find( '.vmwpmdp-wpvoicemessage_gravityforms-additional-fields' )[0].attr( 'user-login' ); 915 let ip = $cForm.find( '.vmwpmdp-wpvoicemessage_gravityforms-additional-fields' )[0].attr( 'user-ip' ); 916 917 if ( ! login ) { 918 login = 'Guest'; 919 } 920 921 vmwpmdpUserDetails.val( login + ' (' + ip + ')' ); 922 923 } 924 925 /** 926 * Handles the "Back" button click, returning to the "Speak Now" step. 927 */ 928 function backButtonClick($cForm) { 929 //console.log('"Back" button clicked for form:', $cForm); 930 931 // Show the "Speak Now" step 932 //showSpeakNowStep($cForm); 933 934 hideAllViews( $cForm ); // Hide all steps. 935 936 // Since the recording was paused (not stopped) before showing the reset view, 937 // we need to resume it here. 938 if (rec) { 939 rec.finishRecording(); 940 //console.log('Recording resumed.'); 941 } 942 943 // Resume timer and visuals 944 isTimerPaused = false; 945 isCountdownPaused = false; 946 drawVisual = window.requestAnimationFrame(draw); 947 948 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-send-box').show(); 949 950 // Optional: Hide any busy or error animations 951 $cForm.removeClass('vmwpmdp-busy'); 952 } 953 954 955 /** 956 * Shows the "Thanks" message and hides all other views for a specific form. 957 */ 958 function showThanksStep($cForm) { 959 //console.log('Showing the thanks step for form:', $cForm); 960 961 // Hide all other views within the form 962 hideAllViews( $cForm ); // Hide all steps. 963 964 // Show the "Thanks" view 965 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-thanks-box').show(); 966 967 968 // Optional: Hide any busy or error animations 969 $cForm.removeClass('vmwpmdp-busy'); 970 } 971 972 function showErrorStep( $cForm ) { 973 hideAllViews( $cForm ); // Hide all steps. 974 $cForm.addClass('vmwpmdp-step-error'); 975 976 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-error-box').show(); 977 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-reset-box').show(); 978 } 979 980 /** 981 * Converted hideAllViews function. 982 * This assumes your views have a common class, e.g., '.vmwpmdp-step'. 983 */ 984 function hideAllViews($cForm) { 985 // Assuming all steps have a common class like '.vmwpmdp-step' 986 987 // Hide all other views within the form 988 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-start-btn-box').hide(); 989 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-allow-access-box').hide(); 990 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-recording-box').hide(); 991 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-reset-box').hide(); 992 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-thanks-box').hide(); 993 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-send-box').hide(); 994 995 // Show the "Send" view 996 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-error-box').hide(); 997 $cForm.find('.vmwpmdp-wpvoicemessage_gravityforms-reset-box').hide(); 998 999 } 1000 1001 1002 1003});
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.