/******/ (() => { // webpackBootstrap
/******/ 	"use strict";
/******/ 	var __webpack_modules__ = ({

/***/ "./wp-content/plugins/contacter/source/js/_events.js":
/*!***********************************************************!*\
  !*** ./wp-content/plugins/contacter/source/js/_events.js ***!
  \***********************************************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   addEvents: () => (/* binding */ addEvents),
/* harmony export */   showStartRecordingStep: () => (/* binding */ showStartRecordingStep),
/* harmony export */   stopAllStreams: () => (/* binding */ stopAllStreams)
/* harmony export */ });
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./_utils */ "./wp-content/plugins/contacter/source/js/_utils.js");
// noinspection DuplicatedCode



/**
 * Add events to the form.
 * @param window
 * @param window.webkitAudioContext
 * @param document
 * @param GreenAudioPlayer
 */

URL = window.URL || window.webkitURL; // webkitURL is deprecated but nevertheless
let gumStream; // Stream from getUserMedia()
let rec; // Recorder.js object
let input; // MediaStreamAudioSourceNode we'll be recording
let AudioContext = window.AudioContext || window.webkitAudioContext;
let audioContext;
let audioBlob;
let drawVisual;
let draw;
let timerInterval;
let countdownInterval;
let isTimerPaused = false;
let isCountdownPaused = false;
let form;
let sampleRate;
let reset = false;
let visitor = false;

function addEvents( cForm ) {

    cForm.addEventListener( 'click', ( e ) => {

        const $target = e.target;
        if ( ! $target ) { return; }

        /** Exit if click on link. */
        if ( $target.href ) {
            return;
        }

        /** Prevent all button clicks except file input type. */
        // if ( e.target.type !== 'file' ) {
        //     e.preventDefault();
        // }

        /** Start Recording Button */
        if ( (0,_utils__WEBPACK_IMPORTED_MODULE_0__.isElement)( $target, 'mdp-contacter-start-btn' ) ) {
            showCountdown( cForm );
        }

        /** Skip record Button */
        if ( (0,_utils__WEBPACK_IMPORTED_MODULE_0__.isElement)( $target, 'mdp-contacter-skip-btn' ) ) {
            stopButtonClick(cForm);
        }

        /** Reset button. */
        if ( (0,_utils__WEBPACK_IMPORTED_MODULE_0__.isElement)( $target, 'mdp-reset-rec-btn' ) ) {

            /** Reset button on Speak Now view. */
            if ( $target.closest( '.mdp-speak-now-btns' ) ) { // .mdp-speak-now-btns .mdp-reset-rec-btn
                resetButtonClick( cForm );
            }

            /** Reset send button on Send view. */
            if ( $target.closest( '.mdp-send-btns' ) ) { // .mdp-send-btns .mdp-reset-rec-btn
                resetSendButtonClick( cForm );
            }

        }

        /** Stop button on Speak Now view. */
        if ( (0,_utils__WEBPACK_IMPORTED_MODULE_0__.isElement)( $target, 'mdp-stop-rec-btn' ) ) { // .mdp-speak-now-btns .mdp-stop-rec-btn
            stopButtonClick( cForm );
        }

        /** Start a New Message. */
        if ( (0,_utils__WEBPACK_IMPORTED_MODULE_0__.isElement)( $target, 'mdp-restart' ) ) { // .mdp-contacter-thanks-box .mdp-restart
            yesResetButtonClick( cForm );
        }

        /** Send button on Send view. */
        if ( (0,_utils__WEBPACK_IMPORTED_MODULE_0__.isElement)( $target, 'mdp-send-rec-btn' ) ) { // .mdp-send-btns .mdp-send-rec-btn
            sendButtonClick( cForm );
        }

        /** Yes button on Reset view. */
        if ( (0,_utils__WEBPACK_IMPORTED_MODULE_0__.isElement)( $target, 'mdp-reset-rec-yes' ) ) { // .mdp-speak-now-btns .mdp-reset-rec-yes
            yesResetButtonClick( cForm );
        }

        /** No button on Reset view. */
        if ( (0,_utils__WEBPACK_IMPORTED_MODULE_0__.isElement)( $target, 'mdp-reset-rec-no' ) ) { // .mdp-speak-now-btns .mdp-reset-rec-no
            noResetButtonClick( e.target, cForm );
        }

        /** Disable form submitting, we use AJAX. */
        form = cForm.querySelector( '.mdp-contacter-send-box form' );
        if ( form ) {
            form.addEventListener( 'submit', ( e ) => {
                e.preventDefault();
            } );
        }

    }, false );

}

/**
 * No button on Reset view.
 **/
function noResetButtonClick( target, cForm ) {

    /** Set the correct target */
    target = target.classList.contains( 'mdp-reset-rec-no' ) ? target : target.parentElement;

    if ( 'showSendStep' === target.getAttribute( 'show-step' ) ) {

        /** Show Send view. */
        showSendStep( cForm );
        target.setAttribute( 'show-step', '' );

    } else {

        /** Resume Recording. */
        rec.record();
        (0,_utils__WEBPACK_IMPORTED_MODULE_0__.log)( 'Recording resumed.' );

        /** Resume timer. */
        isTimerPaused = false;

        /** Resume Countdown. */
        isCountdownPaused = false;

        /** Resume animation. */
        drawVisual = window.requestAnimationFrame( draw );

        /** Show Speak Now view. */
        showSpeakNowStep( cForm );
    }

}

/**
 * Yes button on Reset view.
 **/
function yesResetButtonClick( cForm ) {

    if ( typeof rec !== 'undefined' ){

        /** Stop the recording. */
        rec.stop();

        /** Stop microphone access. */
        gumStream.getAudioTracks()[0].stop();

    }

    /** Clear record */
    reset = true;

    /** Remove all errors messages */
    removeAllErrors( cForm );

    /** Show Start Recording step. */
    showStartRecordingStep( cForm );

}

/**
 * Send button on Send view.
 **/
function sendButtonClick( cForm ) {

    fillCurrentURL( cForm );
    fillUserDetails( cForm );

    let formData;
    const { nonce, ajaxurl } = window.mdpContacterWP;

    cForm.classList.add( 'mdp-busy' ); // Show animation.

    /** Do we have Additional Fields. */
    let form = cForm.querySelector( '.mdp-contacter-send-box form' );
    if ( form ) {

        /** Validate additional fields. */
        if ( ! form.checkValidity() ) {
            form.reportValidity();
            cForm.classList.remove( 'mdp-busy' ); // Hide animation.
            return;
        }

        /** Get Additional fields data. */
        formData = new FormData( form );

        /** There are no Additional Fields. */
    } else {

        /** Get Additional fields data. */
        formData = new FormData();

    }

    /** Send Record if something is recorded */
    if ( ! reset ) {

        /** Add Audio data. */
        formData.append( 'mdp-contacter-audio', audioBlob );

        /** Add audio Sample Rate. */
        formData.append( 'mdp-contacter-audio-sample-rate', sampleRate );

    }

    /** Add Action to process on WP side. */
    formData.append( 'action', 'contacter_send' );

    /** Add nonce to security. */
    formData.append( 'nonce', nonce );

    /** Add cform id. */
    formData.append( 'cform-id', cForm.getAttribute( 'cform-id' ) );

    /** Add IP related info */
    if ( typeof visitor === 'object' ) {

        /**
         * @param {string} visitor.ip
         * @param {string} visitor.country
         * @param {string} visitor.country_name
         * @param {string} visitor.city
         */
        formData.append( 'ip', visitor.ip );
        formData.append( 'country', visitor.country );
        formData.append( 'country_name', visitor.country_name );
        formData.append( 'city', visitor.city );

    }

    /** Send Data to WP. */
    let xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function() {

        if ( xhr.readyState === 4 && xhr.status === 200 ) {

            (0,_utils__WEBPACK_IMPORTED_MODULE_0__.log)( xhr.responseText );

            cForm.classList.remove( 'mdp-busy' ); // Hide animation.

            const wpResponse = JSON.parse( xhr.response );

            /** Render Error message or Render Thanks message */
            if ( wpResponse.status ) {

                /** Show Thanks message. */
                showThanksStep( cForm, wpResponse );

            } else {

                // Remove all errors
                removeAllErrors( cForm );

                // Prepare error nodes
                const $errorWrapper = cForm.querySelector( '.mdp-contacter-send-box form' );
                const $sendButtons = cForm.querySelector( '.mdp-contacter-send-box .mdp-send-btns' );
                const $errorNode = document.createElement( 'p' );

                const errorObj = JSON.parse( xhr.responseText );
                $errorNode.innerText = errorObj.message ?? xhr.responseText;
                $errorNode.classList.add( 'mdp-contacter-error' );

                $errorWrapper.insertBefore( $errorNode, $sendButtons );

            }

        }

    };

    xhr.onload = function() {
        cForm.classList.remove( 'mdp-busy' ); // Hide animation.
    };

    xhr.onerror = function() { // only triggers if the request couldn't be made at all
        cForm.classList.remove( 'mdp-busy' ); // Hide animation.
        (0,_utils__WEBPACK_IMPORTED_MODULE_0__.log)( 'Network Error.', 'error', true );

        /** Show Error message. */
        showErrorStep( cForm );
    };

    cForm.classList.add( 'mdp-busy' ); // Show animation.
    xhr.open( 'POST', ajaxurl, true );
    xhr.send( formData );

}

/**
 * Remove all error messages
 * @param cForm
 */
function removeAllErrors( cForm ) {

    for ( const $oldErrorMessage of cForm.querySelectorAll( '.mdp-contacter-error' ) ) {
        $oldErrorMessage.remove();
    }

}

/**
 * Fill current location to filed if needed.
 **/
function fillCurrentURL( cForm ) {

    let mdpCurrentURL = cForm.querySelectorAll('#mdpCurrentURL')[0];
    if ( 'undefined' === typeof mdpCurrentURL ) { return; }

    mdpCurrentURL.value = window.location.href;

}

/**
 * Fill user details to filed if needed.
 **/
function fillUserDetails( cForm ) {

    let mdpUserDetails = cForm.querySelectorAll( '#mdpUserDetails' )[0];
    if ( 'undefined' === typeof mdpUserDetails ) { return; }

    let login = cForm.querySelectorAll( '.mdp-contacter-additional-fields' )[0].getAttribute( 'user-login' );
    let ip = cForm.querySelectorAll( '.mdp-contacter-additional-fields' )[0].getAttribute( 'user-ip' );

    if ( ! login ) {
        login = 'Guest';
    }

    mdpUserDetails.value = login + ' (' + ip + ')';

}

/**
 * Reset button on Send view.
 **/
function resetSendButtonClick( cForm ) {

    /** Use attribute show-step as flag for No button. */
    cForm.querySelector( '.mdp-speak-now-btns .mdp-reset-rec-no' ).setAttribute( 'show-step', 'showSendStep' );

    /** Show Reset step. */
    showResetStep( cForm );

}

/**
 * Stop Button on Speak Now view.
 **/
function stopButtonClick( cForm ) {

    if ( typeof rec !== 'undefined' ) {

        /** Stop Recording. */
        if ( rec.recording ) { rec.stop() }
        (0,_utils__WEBPACK_IMPORTED_MODULE_0__.log)( 'Recording stopped.' );

        /** Stop timer. */
        clearInterval( timerInterval );

        /** Stop countdown. */
        clearInterval( countdownInterval );

        /** Stop Animation. */
        window.cancelAnimationFrame( drawVisual );

    }

    /** Show Send your recording step. */
    showSendStep( cForm );

    /** Show Additional Fields. */
    showAdditionalFields( cForm );

    if ( typeof rec !== 'undefined' && ! reset ) {

        /** Stop microphone access. */
        gumStream.getAudioTracks()[0].stop();

        /** Create the wav blob and pass it to createPayer. */
        rec.exportWAV( ( blob ) => { createPayer( blob, cForm ) } );

        displayElement( cForm, '.mdp-contacter-download-link-box', 'flex' ); // Show download link
        displayElement( cForm, '.mdp-contacter-player-box', 'flex' ); // Show player

    } else {

        displayElement( cForm, '.mdp-contacter-download-link-box', 'none' ); // Hide download link
        displayElement( cForm, '.mdp-contacter-player-box', 'none' ); // Hide player

    }

}

/**
 * Change display property for element in cForm
 * @param cForm
 * @param element
 * @param display
 */
function displayElement( cForm, element, display ) {

    if ( cForm.querySelector( element ) !== null ) {

        cForm.querySelector( element ).style.display = display;

    }

}

/**
 * Reset Button on Speak Now view.
 **/
function resetButtonClick( cForm ) {

    /** Pause Recording. */
    if ( rec.recording ) { rec.stop(); }
    (0,_utils__WEBPACK_IMPORTED_MODULE_0__.log)( 'Recording paused.' );

    /** Pause timer. */
    isTimerPaused = true;

    /** Pause Countdown. */
    isCountdownPaused = true;

    /** Pause animation. */
    window.cancelAnimationFrame( drawVisual );

    /** Show Reset step. */
    showResetStep( cForm );

}

/**
 * Get recorded audio create player.
 **/
function createPayer( blob, cForm ) {

    let url = URL.createObjectURL( blob );
    audioBlob = blob;
    let audioEl = document.createElement( 'audio' );
    audioEl.src = url;

    /** Add audio element to the player box. */
    const playerBox = cForm.querySelector( '.mdp-contacter-send-box .mdp-contacter-player-box' );
    playerBox.innerHTML = '';
    playerBox.appendChild( audioEl );

    /** Init Green Audio Player. */
    new GreenAudioPlayer( playerBox );

    /** Add Download Link. */
    let downloadLink = cForm.querySelector( '.mdp-contacter-download-link-box > a' );
    if ( ! downloadLink ) { return; }

    const theDate = new Date();
    const timestamp = `${ theDate.getHours() }-${ theDate.getMinutes() }`;
    downloadLink.href = url;
    downloadLink.setAttribute( 'download', `record-${ timestamp }.wav` );

}

/**
 * Show Thank you view (.mdp-contacter-thanks-box).
 **/
function showThanksStep( cForm, wpResponse ) {
    (0,_utils__WEBPACK_IMPORTED_MODULE_0__.hideAllViews)( cForm ); // Hide all steps.

    // Add a record ID if possible
    if ( wpResponse.postID && cForm.querySelector( '.mdp-contacter-thanks-box--post-id' ) ) {

        cForm.querySelector( '.mdp-contacter-thanks-box--post-id b' ).innerText = wpResponse.postID;

    }

    cForm.classList.add( 'mdp-step-thanks' ); // Show Thank you step.

}

/**
 * Show Error view (.mdp-contacter-error-box).
 **/
function showErrorStep( cForm ) {
    (0,_utils__WEBPACK_IMPORTED_MODULE_0__.hideAllViews)( cForm ); // Hide all steps.
    cForm.classList.add( 'mdp-step-error' ); // Show Error step.
}

/**
 * Show Send view (.mdp-contacter-send-box).
 **/
function showSendStep( cForm ) {
    (0,_utils__WEBPACK_IMPORTED_MODULE_0__.hideAllViews)( cForm ); // Hide all steps.
    cForm.classList.add( 'mdp-step-send' ); // Show Send step.
}

/**
 * Show Reset view (.mdp-contacter-reset-box).
 **/
function showResetStep( cForm ) {
    (0,_utils__WEBPACK_IMPORTED_MODULE_0__.hideAllViews)( cForm ); // Hide all steps.
    cForm.classList.add( 'mdp-step-reset' ); // Show Reset step.
}

/**
 * Show countdown box (.mdp-contacter-countdown-box).
 * @param cForm
 */
function showCountdown( cForm ) {

    if ( typeof visitor !== 'object' ) {
        getVisitorInfo();
    }

    (0,_utils__WEBPACK_IMPORTED_MODULE_0__.hideAllViews)( cForm ); // Hide all steps.
    cForm.classList.add( 'mdp-step-countdown' ); // Show Microphone access error step.

    const $counter = cForm.querySelector( '.mdp-contacter-countdown-box--counter' );
    const countdown = parseInt( $counter.innerText );

    // Disable countdown box
    if ( countdown === 0 ) {

        startRecordingButtonClick( cForm );
        return;

    }

    // Run counter animation
    $counter.classList.add( 'mdp-contacter-countdown-run' );

    // Run counter
    let counter = countdown;
    let counterInterval = setInterval( function () {

        counter--;
        $counter.innerHTML = counter.toString();

    }, 1000 );

    // Set delay before recording starts
    setTimeout( function () {

        startRecordingButtonClick( cForm );
        clearInterval( counterInterval );
        $counter.classList.remove( 'mdp-contacter-countdown-run' );
        $counter.innerHTML = countdown.toString(); // Reset counter to the initial value

    }, countdown * 1000);

}

/** Show Additional Fields from additional-fields attribute. */
function showAdditionalFields( cForm ) {

    const additionalFieldsBox = cForm.querySelector( '.mdp-contacter-send-box .mdp-contacter-additional-fields' );

    /** Exit if we don't have Additional Fields. */
    if ( ! additionalFieldsBox ) { return; }

    let additionalFields = additionalFieldsBox.getAttribute( 'additional-fields' );

    additionalFieldsBox.innerHTML = atobUTF8( additionalFields );

}

/**
 * Launches the promise based getUserMedia() and on success it passes the audio stream
 * to an AudioContext which is then passed to our Recorder.js object. The actual recording process
 * is triggered by rec.record().
 **/
function startRecordingButtonClick( cForm ) {

    /** Show Allow Access to microphone view. */
    showAllowAccessStep( cForm );

    const { mdpContacterWP } = window;

    /**
     * We're using the standard promise based getUserMedia()
     * @see: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
     * @see: https://addpipe.com/blog/audio-constraints-getusermedia/
     **/
    navigator.mediaDevices.getUserMedia( { audio: true, video: false } ).then( function( stream ) {

        (0,_utils__WEBPACK_IMPORTED_MODULE_0__.log)( 'getUserMedia() success. Stream created. Initializing Recorder.' );

        /**
         * Create an audio context after getUserMedia is called.
         * SampleRate might change after getUserMedia is called, like it does on macOS when recording through AirPods
         * the sampleRate defaults to the one set in your OS for your playback device.
         **/
        const sampleRate = mdpContacterWP.sampleRate ?? 44100;

        if ( (0,_utils__WEBPACK_IMPORTED_MODULE_0__.isFirefox)() ) {

            /** Firefox doesn't support the AudioContext constructor without any arguments. */
            audioContext = new AudioContext();

        } else {

            /** Chrome and other browsers. */
            audioContext = new AudioContext({
                latencyHint: "interactive",
                sampleRate: sampleRate,
            });

        }

        /** Log the format. */
        (0,_utils__WEBPACK_IMPORTED_MODULE_0__.log)( 'Format: 1 channel pcm @ ' + audioContext.sampleRate/1000 + 'kHz' );

        /** Assign to gumStream for later use.  */
        gumStream = stream;

        /** Use the stream. */
        input = audioContext.createMediaStreamSource( stream );

        /**
         * Create the Recorder object and configure to record mono sound (1 channel)
         * Recording 2 channels  will double the file size.
         **/
        rec = new Recorder( input,{ numChannels: 1 } );

        /** Start the recording. */
        rec.record();
        (0,_utils__WEBPACK_IMPORTED_MODULE_0__.log)( 'Recording started.' );

        /** Show Speak Now view. */
        showSpeakNowStep( cForm );

        /** Create Timer. */
        createTimer( cForm );

        /** Create Countdown. */
        createCountdown( cForm );

        /** Create Animation. */
        createAnimation( cForm );

    } ).catch( function( err ) {

        setTimeout( function (){

            /** Show Error if getUserMedia() fails. */
            (0,_utils__WEBPACK_IMPORTED_MODULE_0__.log)( 'Error getUserMedia() fails. See details below.', 'warn', true );
            (0,_utils__WEBPACK_IMPORTED_MODULE_0__.log)( err, 'error', true );

            /** Show Microphone access error step if getUserMedia() fails. */
            showMicrophoneAccessErrStep( cForm );

        }, 1000 );

    } );

}

/**
 * Create Animation.
 **/
function createAnimation( cForm ) {

    /**
     * Create Analyser to extract data from audio source.
     * The AnalyserNode interface represents a node able to provide real-time frequency and time-domain analysis information.
     **/
    let analyser = audioContext.createAnalyser();

    /** Connect analyser to audio source. */
    input.connect( analyser );

    /** Array to receive the data from audio source. */
    analyser.fftSize = 2048;
    let bufferLength = analyser.frequencyBinCount;
    let dataArray = new Uint8Array( bufferLength );

    /** Canvas for animation. */
    let animation = cForm.querySelector( '.mdp-contacter-recording-animation canvas' );

    let animationCtx = animation.getContext( "2d" );

    /** Clear the canvas. */
    animationCtx.clearRect( 0, 0, animation.width, animation.height );

    draw = function() {

        const { mdpContacterWP } = window;

        /** Using requestAnimationFrame() to keep looping the drawing function once it has been started. */
        drawVisual = requestAnimationFrame( draw );

        /** Grab the time domain data and copy it into our array. */
        analyser.getByteTimeDomainData( dataArray );

        /** Fill the canvas with a solid colour to start. */
        animationCtx.clearRect( 0, 0, animation.width, animation.height ); // Clear the canvas.
        animationCtx.fillStyle = 'rgba( 255, 255, 255, 0.01 )'; // Almost transparent
        animationCtx.fillRect( 0, 0, animation.width, animation.height );

        /** Set a line width and stroke colour for the wave we will draw, then begin drawing a path. */
        animationCtx.lineWidth = 2;

        let startColor = mdpContacterWP.accentColor.replace(/[^,]+(?=\))/, '0');
        let endColor = mdpContacterWP.accentColor.replace(/[^,]+(?=\))/, '1');

        const gradient = animationCtx.createLinearGradient(0, 0, 384, 0);
        gradient.addColorStop( 0, startColor );
        gradient.addColorStop( .25 , endColor );
        gradient.addColorStop( .75 , endColor );
        gradient.addColorStop( 1, startColor );
        animationCtx.strokeStyle = gradient;

        animationCtx.beginPath();

        /**
         * Determine the width of each segment of the line to be drawn
         * by dividing the canvas width by the array length (equal to the FrequencyBinCount, as defined earlier on),
         * then define an x variable to define the position to move to for drawing each segment of the line.
         **/
        let sliceWidth = animation.width * 1.0 / bufferLength;
        let x = 0;

        /**
         * Run through a loop, defining the position of a small segment of the wave
         * for each point in the buffer at a certain height based on the data point value form the array,
         * then moving the line across to the place where the next wave segment should be drawn.
         **/
        for ( let i = 0; i < bufferLength; i++ ) {

            let v = dataArray[i] / 128.0;
            let y = v * animation.height/2;

            if ( i === 0 ) {
                animationCtx.moveTo( x, y );
            } else {
                animationCtx.lineTo( x, y );
            }

            x += sliceWidth;
        }

        /**
         * Finish the line in the middle of the right hand side of the canvas,
         * then draw the stroke we've defined.
         **/
        animationCtx.lineTo( animation.width, animation.height/2 );
        animationCtx.stroke();
    };

    /** Call the draw() function to start off the whole process. */
    draw();

}

/**
 * Create Countdown.
 **/
function createCountdown( cForm ) {

    const countdownElement = cForm.querySelector( '.mdp-contacter-recording-countdown' );

    if ( null === countdownElement ) { return; }

    let maxDuration = parseInt( cForm.getAttribute( 'max-duration' ) );

    if ( maxDuration === 0 ) { return; } // If maxDuration set to unlimited, countdown is impossible.

    let countdown = maxDuration;

    /** Reset previously countdowns. */
    clearInterval( countdownInterval );
    isCountdownPaused = false;
    let resetMinutes = Math.floor( maxDuration / 60 );
    let resetSeconds = maxDuration - resetMinutes * 60;
    countdownElement.innerHTML = resetMinutes.pad( 2 ) + ':' + resetSeconds.pad( 2 );

    /** Start new countdown. */
    countdownInterval = setInterval( function () {

        if ( isCountdownPaused ) { return; } // Pause.

        countdown--;

        /** If timer lower than 0 Stop recording. */
        if ( maxDuration !== 0 && countdown < 0 ) {
            cForm.querySelector( '.mdp-speak-now-btns .mdp-stop-rec-btn' ).click();
        }

        let minutes = Math.floor( countdown / 60 );
        let seconds = countdown - minutes * 60;

        countdownElement.innerHTML = minutes.pad( 2 ) + ':' + seconds.pad( 2 );

    }, 1000 );

}

/**
 * Create Timer.
 **/
function createTimer( cForm ) {
    let timer = 0;
    const timerElement = cForm.querySelector( '.mdp-contacter-recording-timer' );

    if ( null === timerElement ) { return; }

    let maxDuration = parseInt( cForm.getAttribute( 'max-duration' ) );

    /** Reset previously timers. */
    clearInterval( timerInterval );
    isTimerPaused = false;
    timerElement.innerHTML = '00:00';

    /** Start new timer. */
    timerInterval = setInterval( function () {

        if ( isTimerPaused ) { return; } // Pause.

        timer++;

        /** If timer bigger than max-duration Stop recording. */
        if ( maxDuration !== 0 && timer > maxDuration ) {
            cForm.querySelector( '.mdp-speak-now-btns .mdp-stop-rec-btn' ).click();
        }

        let minutes = Math.floor( timer / 60 );
        let seconds = timer - minutes * 60;

        timerElement.innerHTML = minutes.pad( 2 ) + ':' + seconds.pad( 2 );

    }, 1000 );
}

/**
 * Show Allow Access to microphone view (.mdp-contacter-allow-access-box).
 **/
function showAllowAccessStep( cForm ) {
    (0,_utils__WEBPACK_IMPORTED_MODULE_0__.hideAllViews)( cForm ); // Hide all steps.
    cForm.classList.add( 'mdp-step-allow-access' ); // Show Allow Access step.
}

/**
 * Show Microphone access error view (.mdp-contacter-mic-access-err-box).
 **/
function showMicrophoneAccessErrStep( cForm ) {
    (0,_utils__WEBPACK_IMPORTED_MODULE_0__.hideAllViews)( cForm ); // Hide all steps.
    cForm.classList.add( 'mdp-step-mic-access-err' ); // Show Microphone access error step.
}

/**
 * Show Speak Now view (.mdp-contacter-recording-box).
 **/
function showSpeakNowStep( cForm ) {
    (0,_utils__WEBPACK_IMPORTED_MODULE_0__.hideAllViews)( cForm ); // Hide all steps.
    cForm.classList.add( 'mdp-step-recording' ); // Show Recording step.
    reset = false;
}

/**
 * Show Start Recording view (.mdp-contacter-start-box).
 **/
function showStartRecordingStep( cForm ) {
    (0,_utils__WEBPACK_IMPORTED_MODULE_0__.hideAllViews)( cForm ); // Hide all steps.
    const $resumeButton = cForm.querySelector( '.mdp-speak-now-btns .mdp-reset-rec-no' );
    if ( $resumeButton.getAttribute( 'show-step' ) ) {
        $resumeButton.removeAttribute( 'show-step' );
    }
    cForm.classList.add( 'mdp-step-start' ); // Show Start Recording step.
}

/**
 * Get visitor ip, country and other info
 */
function getVisitorInfo() {

    let xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function () {

        if ( xhttp.readyState === 4 && xhttp.status === 200 ) {

            visitor = JSON.parse( xhttp.response );

        }

    }
    xhttp.open( 'GET', 'https://ipapi.co/json/', true );
    xhttp.send();

}

/**
 * Stop all media stream.
 **/
function stopAllStreams() {

    if ( typeof gumStream !== 'undefined' ) {
        gumStream.getTracks().forEach( function( track) {
            track.stop();
        } );
    }

}


/***/ }),

/***/ "./wp-content/plugins/contacter/source/js/_observer.js":
/*!*************************************************************!*\
  !*** ./wp-content/plugins/contacter/source/js/_observer.js ***!
  \*************************************************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   observer: () => (/* binding */ observer)
/* harmony export */ });
/* harmony import */ var _events__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./_events */ "./wp-content/plugins/contacter/source/js/_events.js");


function observer() {

    const targetNode = document.body;
    const config = { attributes: true, childList: true, subtree: true };

    // Callback function to execute when mutations are observed
    const callback = ( mutationList ) => {

        for ( const mutation of mutationList ) {

            // Catch only childList mutations
            if ( mutation.type !== 'childList' ) { continue; }

            // Catch only added nodes
            const addedNodes = mutation.addedNodes ?? [];
            addedNodes.forEach( addedNode => {

                // Catch only dialog-widget nodes
                if ( addedNode.classList && addedNode.classList.contains( 'dialog-widget' ) ) {

                    // Catch only contacter form boxes
                    const contacterFormBoxes = addedNode.querySelectorAll( '.mdp-contacter-form-box' );
                    if ( contacterFormBoxes ) {

                        contacterFormBoxes.forEach( cForm => (0,_events__WEBPACK_IMPORTED_MODULE_0__.addEvents)( cForm ) );

                    }

                }

            } );


        }

    };

    const observer = new MutationObserver( callback );
    observer.observe( targetNode, config );

}


/***/ }),

/***/ "./wp-content/plugins/contacter/source/js/_utils.js":
/*!**********************************************************!*\
  !*** ./wp-content/plugins/contacter/source/js/_utils.js ***!
  \**********************************************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   hideAllViews: () => (/* binding */ hideAllViews),
/* harmony export */   isElement: () => (/* binding */ isElement),
/* harmony export */   isFirefox: () => (/* binding */ isFirefox),
/* harmony export */   log: () => (/* binding */ log)
/* harmony export */ });
/**
 * Check if the target is the element we want
 * If element has class or parent has class return true, else false
 * @param $target
 * @param className
 * @returns {boolean}
 */
function isElement( $target, className ) {

    return $target.classList.contains( className ) || $target.parentElement.classList.contains( className );

}

/**
 * Hide all step boxes, by removing all step classes from element.
 *
 * @param {object} el - Current Contacter Form (.mdp-contacter-form-box)
 **/
function hideAllViews( el ) {

    el.classList.remove( 'mdp-step-countdown' );
    el.classList.remove( 'mdp-step-start' );
    el.classList.remove( 'mdp-step-allow-access' );
    el.classList.remove( 'mdp-step-mic-access-err' );
    el.classList.remove( 'mdp-step-recording' );
    el.classList.remove( 'mdp-step-reset' );
    el.classList.remove( 'mdp-step-error' );
    el.classList.remove( 'mdp-step-send' );
    el.classList.remove( 'mdp-step-thanks' );

}

/**
 * Show log message in console if debug is enabled.
 *
 * @param {string} msg - Message to log in console. Required.
 * @param {string} level - Log level: log, warn, error. Optional.
 * @param {boolean} always - True to always enabled message. Optional.
 *
 * @return {void}
 **/
function log( msg, level = 'log', always = false ) {

    /** Enable debug by URL. */
    let debug = getUrlVars()['debug'];
    debug = typeof debug !== 'undefined';

    /** Enable debug by second params. */
    if ( always ) {
        debug = true;
    }

    // debug = true; // Uncomment this line to enable logging manually.

    /** Exit if debug is disabled. */
    if ( ! debug ) { return; }

    /** Add plugin name to identify messages. */
    msg = 'Contacter: ' + msg;

    if ( 'log' === level ) {
        console.info( msg );

    } else if ( 'warn' === level ) {
        console.warn( msg );

    } else if ( 'error' === level ) {
        console.error( msg );
    }

}

/**
 * Parse and return current URL parameters.
 **/
function getUrlVars() {
    let vars = {};
    window.location.href.replace( /[?&]+([^=&]+)=([^&]*)/gi, function( m, key, value ) {
        vars[key] = value;
    } );

    return vars;
}

/**
 * Is firefox browser
 */
function isFirefox() {

    return navigator.userAgent.toLowerCase().indexOf( 'firefox' ) > -1;

}


/***/ })

/******/ 	});
/************************************************************************/
/******/ 	// The module cache
/******/ 	var __webpack_module_cache__ = {};
/******/ 	
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/ 		// Check if module is in cache
/******/ 		var cachedModule = __webpack_module_cache__[moduleId];
/******/ 		if (cachedModule !== undefined) {
/******/ 			return cachedModule.exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = __webpack_module_cache__[moduleId] = {
/******/ 			// no module.id needed
/******/ 			// no module.loaded needed
/******/ 			exports: {}
/******/ 		};
/******/ 	
/******/ 		// Execute the module function
/******/ 		__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/ 	
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/ 	
/************************************************************************/
/******/ 	/* webpack/runtime/define property getters */
/******/ 	(() => {
/******/ 		// define getter functions for harmony exports
/******/ 		__webpack_require__.d = (exports, definition) => {
/******/ 			for(var key in definition) {
/******/ 				if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ 					Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ 				}
/******/ 			}
/******/ 		};
/******/ 	})();
/******/ 	
/******/ 	/* webpack/runtime/global */
/******/ 	(() => {
/******/ 		__webpack_require__.g = (function() {
/******/ 			if (typeof globalThis === 'object') return globalThis;
/******/ 			try {
/******/ 				return this || new Function('return this')();
/******/ 			} catch (e) {
/******/ 				if (typeof window === 'object') return window;
/******/ 			}
/******/ 		})();
/******/ 	})();
/******/ 	
/******/ 	/* webpack/runtime/hasOwnProperty shorthand */
/******/ 	(() => {
/******/ 		__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ 	})();
/******/ 	
/******/ 	/* webpack/runtime/make namespace object */
/******/ 	(() => {
/******/ 		// define __esModule on exports
/******/ 		__webpack_require__.r = (exports) => {
/******/ 			if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ 				Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ 			}
/******/ 			Object.defineProperty(exports, '__esModule', { value: true });
/******/ 		};
/******/ 	})();
/******/ 	
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
(() => {
/*!*************************************************************!*\
  !*** ./wp-content/plugins/contacter/source/js/contacter.js ***!
  \*************************************************************/
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _observer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./_observer */ "./wp-content/plugins/contacter/source/js/_observer.js");
/* harmony import */ var _events__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./_events */ "./wp-content/plugins/contacter/source/js/_events.js");
/**
 * Contacter
 * Voice feedback form for your website for saving and transcribing user voice messages to text.
 * Exclusively on https://1.envato.market/contacter
 *
 * @encoding        UTF-8
 * @version         1.7.8
 * @copyright       (C) 2018 - 2023 Merkulove ( https://merkulov.design/ ). All rights reserved.
 * @license         Envato License https://1.envato.market/KYbje
 * @contributors    Dmitry Merkulov (dmitry@merkulov.design)
 * @support         help@merkulov.design
 **/






/**
 * @param window.mdpContacterWP
 */
document.addEventListener('DOMContentLoaded', function() {

    /** Open popup. */
    let fbutton = document.getElementById( 'mdp-contacter-fbutton' );
    if ( null !== fbutton ) {
        fbutton.addEventListener( 'click', openPopup );
    }

    /**
     * Fix FOUC.
     */
    document.querySelectorAll( '.mdp-contacter-form-box, .mdp-contacter-fbutton-box' ).forEach( ( $el ) => {

        if ( $el.getAttribute( 'style' ) ) {
            $el.removeAttribute( 'style' );
        }

    } );

    /** Close modal by overlay click. */
    let overlay = document.querySelector( '.mdp-contacter-fbutton-overlay' );
    if ( null !== overlay ) {
        overlay.addEventListener( 'click', closePopup, false );

        /** Close modal by close button click. */
        overlay.querySelectorAll( '.mdp-contacter-fbutton-modal .mdp-contacter-close' )[0].addEventListener( 'click', closePopup, false );
    }

    /**
     * Do not close popup by click on window
     * and stop event propagation to overlay.
     **/
    let popup = document.querySelector( '.mdp-contacter-fbutton-modal' );
    if ( null !== popup ) {
        popup.addEventListener( 'click', ( event ) => { event.stopPropagation(); }, false );
    }

    /** Add Events to buttons. */
    let startRecordButtons = document.querySelectorAll( '.mdp-contacter-form-box .mdp-contacter-start-btn' );
    for ( const recordBtn of startRecordButtons ) {

        /** Current Contacter Form */
        const cForm = recordBtn.closest( '.mdp-contacter-form-box' );
        if ( ! cForm ) { continue; }

        (0,_events__WEBPACK_IMPORTED_MODULE_1__.addEvents)( cForm );

    }

    /** Observe contacter shortcodes added in popup after page load. */
    (0,_observer__WEBPACK_IMPORTED_MODULE_0__.observer)();

    /** Try again button after microphone access error. */
    let micTryAgainBts = document.querySelectorAll( '.mdp-contacter-form-box .mdp-mic-access-err-reload-btn' );
    for ( const micTryAgainBtn of micTryAgainBts ) {
        micTryAgainBtn.addEventListener( 'click', ()=>{ location.reload(); } );
    }

    /**
     * Open Popup.
     **/
    function openPopup() {

        let root = document.getElementsByTagName( 'html' )[0];
        let overlay = document.querySelector( '.mdp-contacter-fbutton-overlay' );
        overlay.style.display = 'block';
        root.classList.add( 'mdp-contacter-modal-opened' );

    }

    /**
     * Close Popup.
     **/
    function closePopup() {

        let root = document.getElementsByTagName( 'html' )[0];
        let overlay = document.querySelector( '.mdp-contacter-fbutton-overlay' );

        /** Reverse animation */
        root.classList.add( 'mdp-contacter-reverse' );

        /** Hide all */
        setTimeout( function () {

            overlay.style.display = 'none';
            root.classList.remove( 'mdp-contacter-modal-opened' );
            root.classList.remove( 'mdp-contacter-reverse' );

            /** Show Start Recording step. */
            let pop_cForm = document.querySelector( '.mdp-contacter-fbutton-modal .mdp-contacter-form-box' );
            (0,_events__WEBPACK_IMPORTED_MODULE_1__.showStartRecordingStep)( pop_cForm );

            /** Stop all media stream. */
            (0,_events__WEBPACK_IMPORTED_MODULE_1__.stopAllStreams)();

        }, 600 );

    }

    /**
     * Output numbers with leading zeros.
     *
     * @param {number} size - A number of digits.
     **/
    Number.prototype.pad = function( size ) {

        let s = String( this );

        while ( s.length < ( size || 2 ) ) { s = '0' + s; }

        return s;

    }

} );

/**
 * The most standard, most cross-browser, most compact,
 * and fastest possible btoa and atob solution for unicode strings with high code points.
 *
 * @see: https://github.com/anonyco/BestBase64EncoderDecoder
 **/
(function(window){
    "use strict";
    var log = Math.log;
    var LN2 = Math.LN2;
    var clz32 = Math.clz32 || function(x) {return 31 - log(x >>> 0) / LN2 | 0};
    var fromCharCode = String.fromCharCode;
    var originalAtob = atob;
    var originalBtoa = btoa;
    function btoaReplacer(nonAsciiChars){
        // make the UTF string into a binary UTF-8 encoded string
        var point = nonAsciiChars.charCodeAt(0);
        if (point >= 0xD800 && point <= 0xDBFF) {
            var nextcode = nonAsciiChars.charCodeAt(1);
            if (nextcode !== nextcode) // NaN because string is 1 code point long
                return fromCharCode(0xef/*11101111*/, 0xbf/*10111111*/, 0xbd/*10111101*/);
            // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
            if (nextcode >= 0xDC00 && nextcode <= 0xDFFF) {
                point = (point - 0xD800) * 0x400 + nextcode - 0xDC00 + 0x10000;
                if (point > 0xffff)
                    return fromCharCode(
                        (0x1e/*0b11110*/<<3) | (point>>>18),
                        (0x2/*0b10*/<<6) | ((point>>>12)&0x3f/*0b00111111*/),
                        (0x2/*0b10*/<<6) | ((point>>>6)&0x3f/*0b00111111*/),
                        (0x2/*0b10*/<<6) | (point&0x3f/*0b00111111*/)
                    );
            } else return fromCharCode(0xef, 0xbf, 0xbd);
        }
        if (point <= 0x007f) return nonAsciiChars;
        else if (point <= 0x07ff) {
            return fromCharCode((0x6<<5)|(point>>>6), (0x2<<6)|(point&0x3f));
        } else return fromCharCode(
            (0xe/*0b1110*/<<4) | (point>>>12),
            (0x2/*0b10*/<<6) | ((point>>>6)&0x3f/*0b00111111*/),
            (0x2/*0b10*/<<6) | (point&0x3f/*0b00111111*/)
        );
    }
    window["btoaUTF8"] = function(inputString, BOMit){
        return originalBtoa((BOMit ? "\xEF\xBB\xBF" : "") + inputString.replace(
            /[\x80-\uD7ff\uDC00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]?/g, btoaReplacer
        ));
    }
    //////////////////////////////////////////////////////////////////////////////////////
    function atobReplacer(encoded){
        var codePoint = encoded.charCodeAt(0) << 24;
        var leadingOnes = clz32(~codePoint);
        var endPos = 0, stringLen = encoded.length;
        var result = "";
        if (leadingOnes < 5 && stringLen >= leadingOnes) {
            codePoint = (codePoint<<leadingOnes)>>>(24+leadingOnes);
            for (endPos = 1; endPos < leadingOnes; ++endPos)
                codePoint = (codePoint<<6) | (encoded.charCodeAt(endPos)&0x3f/*0b00111111*/);
            if (codePoint <= 0xFFFF) { // BMP code point
                result += fromCharCode(codePoint);
            } else if (codePoint <= 0x10FFFF) {
                // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
                codePoint -= 0x10000;
                result += fromCharCode(
                    (codePoint >> 10) + 0xD800,  // highSurrogate
                    (codePoint & 0x3ff) + 0xDC00 // lowSurrogate
                );
            } else endPos = 0; // to fill it in with INVALIDs
        }
        for (; endPos < stringLen; ++endPos) result += "\ufffd"; // replacement character
        return result;
    }
    window["atobUTF8"] = function(inputString, keepBOM){
        if (!keepBOM && inputString.substring(0,3) === "\xEF\xBB\xBF")
            inputString = inputString.substring(3); // eradicate UTF-8 BOM
        // 0xc0 => 0b11000000; 0xff => 0b11111111; 0xc0-0xff => 0b11xxxxxx
        // 0x80 => 0b10000000; 0xbf => 0b10111111; 0x80-0xbf => 0b10xxxxxx
        return originalAtob(inputString).replace(/[\xc0-\xff][\x80-\xbf]*/g, atobReplacer);
    };
})(typeof __webpack_require__.g == "" + void 0 ? typeof self == "" + void 0 ? undefined : self : __webpack_require__.g);

})();

/******/ })()
;