HEX
Server: LiteSpeed
System: Linux server315.web-hosting.com 4.18.0-553.54.1.lve.el8.x86_64 #1 SMP Wed Jun 4 13:01:13 UTC 2025 x86_64
User: globfdxw (6114)
PHP: 8.1.34
Disabled: NONE
Upload Files
File: /home/globfdxw/public_html/wp-content/plugins/wpforms-pdf/assets/js/modules/preview.js
/* global WPForms, wpforms_builder, WPFormsBuilder, WPFormsPDFBuilder, wpf */

/**
 * @param wpforms_builder.pdf_preview_back
 * @param wpforms_builder.pdf_preview_title
 * @param wpforms_builder.pdf_preview_subtitle
 * @param wpforms_builder.pdf_preview_error
 * @param wpforms_builder.pdf_preview_zoom
 * @param WPFormsPDFBuilder.previewReflector
 */

/**
 * WPForms PDF: Preview Module.
 *
 * @since 1.0.0
 *
 * @param {Object} document Document object.
 * @param {Object} window   Window object.
 * @param {jQuery} $        jQuery object.
 *
 * @return {Object} Public functions and properties.
 */
export default function( document, window, $ ) { // eslint-disable-line no-unused-vars, max-lines-per-function
	/**
	 * Reflector object.
	 *
	 * Handles connection between PDF settings and PDF preview.
	 *
	 * @since 1.0.0
	 *
	 * @type {Object}
	 */
	let reflector = {};

	/**
	 * Public functions and properties.
	 *
	 * @since 1.0.0
	 *
	 * @type {Object}
	 */
	const app = {

		/**
		 * Start the engine.
		 *
		 * @since 1.0.0
		 */
		init() {
			app.ready();
		},

		/**
		 * Document ready.
		 *
		 * @since 1.0.0
		 */
		ready() {
			app.setup();
			app.events();
		},

		/**
		 * Setup cache.
		 *
		 * @since 1.0.0
		 */
		setup() {
			// Cache DOM elements here.
			app.el = {
				$doc: $( document ),
				$builder: $( '#wpforms-builder' ),
				$sidebar: $( '#wpforms-panel-settings .wpforms-panel-sidebar' ),
				$settings: $( '.wpforms-panel-content-section-pdf' ),
				$form: $( '#wpforms-builder-form' ),
				$preview: $( '#wpforms-pdf-preview' ),
				$previewZoom: $( '#wpforms-pdf-preview-zoom' ),
			};

			app.formId = app.el.$form.data( 'id' );
			app.loadingPreviews = [];

			reflector = WPFormsPDFBuilder.previewReflector;
			reflector.initPairDefinitions();

			app.fetchPreviewAjaxDebounced = _.debounce( app.fetchPreviewAjax, 250 );
			app.handleSettingInputDebounced = _.debounce( app.handleSettingInput, 250 );
		},

		/**
		 * Bind events.
		 *
		 * @since 1.0.0
		 */
		events() {
			app.el.$doc
				// Initialize previews when the builder is ready.
				.on( 'wpformsBuilderReady', app.initializePreviews )
				// Firefox fix. Reinitialize previews when the PDF modules are loaded.
				.on( 'wpformsPDFLoaded', app.initializePreviews )
				.on( 'tinymce-editor-init', app, app.initTinyMCEevents );

			app.el.$builder
				.on( 'click', '#wpforms-pdf-preview-back', app.handleClosePreviewSidebar )
				.on( 'click', '#wpforms-pdf-preview-zoom', app.handleClickZoomButton )
				.on( 'click', '#wpforms-pdf-preview-refresh', app.handleRefreshPreview )
				.on( 'click', '#wpforms-pdf-preview', app.handlePreviewClick )
				.on( 'click', '.wpforms-panel-sidebar-section-pdf', app.handleOpenPreviewSidebar )
				.on( 'wpformsSettingsBlockAdded wpformsSettingsBlockCloned', app.pdfAdded )
				.on( 'wpformsSettingsBlockDeleted', app.handleSettingsBlockDeleted )
				.on( 'wpformsPDFTemplateStyleChange', app.handleTemplateStyleChange )
				.on( 'wpformsPDFTemplateStyleChanged', app.handleTemplateStyleChanged );

			// Handle all the settings events.
			const changeSelector = '.wpforms-pdf.wpforms-builder-settings-block :input';
			const nonColorInputSelector = changeSelector + ':not( .minicolors-input )';
			const colorInputSelector = changeSelector + '.minicolors-input';
			const focusSelector = changeSelector + `,
				 .wpforms-pdf.wpforms-builder-settings-block .wpforms-smart-tags-widget,
				 .wpforms-pdf.wpforms-builder-settings-block .choices`;

			app.el.$builder
				.on( 'focus click', focusSelector, app.handleSettingFocus )
				.on( 'blur', focusSelector, app.handleSettingBlur )
				.on( 'input change', nonColorInputSelector, app.handleSettingInput )
				.on( 'change', colorInputSelector, app.handleSettingInputDebounced )
				.on( 'wpformsImageUploadChange', app.handleImageUploadChange )
				.on( 'mousedown', '.wpforms-image-remove-button', app.handleBeforeImageRemove );
		},

		/**
		 * Handle TinyMCE init event.
		 *
		 * @since 1.0.0
		 *
		 * @param {Event}  event  Event.
		 * @param {Object} editor TinyMCE editor object.
		 */
		initTinyMCEevents( event, editor ) {
			if ( ! editor.id.startsWith( 'wpforms-panel-field-pdfs-' ) ) {
				return;
			}

			/**
			 * @property {Object} targetElm TinyMCE target element.
			 */
			editor.on( 'focus', function() {
				app.handleSettingFocus.call( this.targetElm );
			} );

			editor.on( 'blur', function() {
				editor.save();
				app.handleSettingBlur.call( this.targetElm );
			} );

			editor.on( 'keyup change paste', function() {
				editor.save();
				app.handleSettingInput.call( this.targetElm );
			} );
		},

		/**
		 * Initialize previews for all existing PDF blocks.
		 *
		 * @since 1.0.0
		 */
		initializePreviews() {
			if ( app.isPreviewsInitialized ) {
				return;
			}

			let pdfId = null;

			app.el.$settings.find( '.wpforms-builder-settings-block' ).each( function() {
				app.addPreviewIframe( this );

				pdfId = pdfId ?? $( this ).data( 'block-id' );
			} );

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

			app.isPreviewsInitialized = true;

			if ( wpf.getQueryString( 'section' ) === 'pdf' ) {
				app.handleOpenPreviewSidebar( null );
			}

			app.activatePreviewIframe( pdfId );
			app.showSpinner( pdfId );
		},

		/**
		 * Get all PDF settings blocks.
		 *
		 * @since 1.0.0
		 *
		 * @return {jQuery} Collection of PDF settings blocks.
		 */
		getBlocks() {
			return app.el.$settings.find( '.wpforms-builder-settings-block' );
		},

		/**
		 * Create the preview sidebar.
		 *
		 * @since 1.0.0
		 */
		maybeCreatePreviewSidebar() {
			if ( app.el.$previewSidebar?.length ) {
				return;
			}

			// Add the buttons visible only in debug mode.
			const debugButtons = wpforms_builder.debug
				? `<button id="wpforms-pdf-preview-refresh" class="wpforms-pdf-preview-button"><i class="fa fa-refresh" aria-hidden="true"></i></button>`
				: '';

			// Add the PDF preview sidebar.
			app.el.$previewSidebar = $(
				// language=HTML
				`<div id="wpforms-pdf-preview-sidebar" class="wpforms-hidden">
					<div class="wpforms-pdf-preview-head">
						<button id="wpforms-pdf-preview-back">${ _.escape( wpforms_builder.pdf_preview_back ) }</button>
					</div>
					<div id="wpforms-pdf-preview-body">
						<div class="wpforms-pdf-preview-title">
							<h4>${ _.escape( wpforms_builder.pdf_preview_title ) }</h4>
							<p>${ _.escape( wpforms_builder.pdf_preview_subtitle ) }</p>
						</div>
						<div id="wpforms-pdf-preview">
							<div id="wpforms-pdf-preview-spinner">
								<i class="wpforms-loading-spinner wpforms-loading-lg"></i>
							</div>
						</div>
						<button id="wpforms-pdf-preview-zoom" class="wpforms-pdf-preview-button">${ _.escape( wpforms_builder.pdf_preview_zoom ) }</button>
						${ debugButtons }
					</div>
				</div>`
			);

			app.el.$sidebar.after( app.el.$previewSidebar );

			app.el.$previewBody = $( '#wpforms-pdf-preview-body' );
			app.el.$preview = $( '#wpforms-pdf-preview' );
			app.el.$previewBack = $( '#wpforms-pdf-preview-back' );
			app.el.$previewSpinner = $( '#wpforms-pdf-preview-spinner' );
			app.el.$previewZoom = $( '#wpforms-pdf-preview-zoom' );

			// Compact scrollbar for non-Mac devices.
			app.el.$previewBody.toggleClass(
				'wpforms-scrollbar-compact',
				! navigator.userAgent.includes( 'Macintosh' ) && ! navigator.userAgent.includes( 'Firefox' )
			);
		},

		/**
		 * Add the preview container below a specific PDF settings section.
		 *
		 * @since 1.0.0
		 *
		 * @param {HTMLElement|jQuery} block The settings block.
		 */
		addPreviewIframe( block ) {
			const $block = $( block );
			const pdfId = $block.data( 'block-id' );
			const $iframe = app.getPreviewIframe( pdfId );

			// If an iframe already exists, do nothing.
			if ( app.el.$preview?.length && $iframe.length ) {
				return;
			}

			app.maybeCreatePreviewSidebar();

			// Add the PDF preview iframe.
			app.el.$preview.append( `<iframe class="wpforms-pdf-preview-iframe" id="wpforms-pdf-preview-iframe-${ pdfId }" scrolling="no"></iframe>` );

			app.initPreviewPaper( pdfId );

			// Fetch the PDF preview HTML.
			app.fetchPreview( pdfId, false );
		},

		/**
		 * Remove the preview iframe for a specific PDF settings section.
		 *
		 * @since 1.0.0
		 *
		 * @param {string} pdfId The ID of the PDF settings section.
		 */
		removePreviewIframe( pdfId ) {
			$( '#wpforms-pdf-preview-iframe-' + pdfId ).remove();
		},

		/**
		 * Activate the preview iframe.
		 *
		 * @since 1.0.0
		 *
		 * @param {string} pdfId The ID of the PDF settings section.
		 */
		activatePreviewIframe( pdfId ) {
			if ( ! pdfId ) {
				return;
			}

			app.initPreviewPaper( pdfId );

			app.el.$preview.find( '.wpforms-pdf-preview-iframe.active' ).removeClass( 'active' );
			app.el.$preview.find( `#wpforms-pdf-preview-iframe-${ pdfId }` ).addClass( 'active' );
		},

		/**
		 * Initialize the preview paper size and orientation.
		 *
		 * @since 1.0.0
		 *
		 * @param {string} pdfId The ID of the PDF settings section.
		 */
		initPreviewPaper( pdfId ) {
			app.el.$preview.attr( 'data-paper-size', $( `#wpforms-panel-field-pdfs-${ pdfId }-paper_size` ).val() );
			app.el.$preview.attr( 'data-orientation', $( `#wpforms-panel-field-pdfs-${ pdfId }-orientation` ).val() );
		},

		/**
		 * Get the preview iframe document for a specific PDF settings section.
		 *
		 * @since 1.0.0
		 *
		 * @param {string|null} pdfId The ID of the PDF settings section.
		 *
		 * @return {jQuery} The preview iframe document.
		 */
		getPreviewIframe( pdfId ) {
			let $iframe = null;

			if ( ! pdfId ) {
				$iframe = app.el.$preview.find( '.wpforms-pdf-preview-iframe.active' );
			} else {
				$iframe = $( '#wpforms-pdf-preview-iframe-' + pdfId );
			}

			return $iframe;
		},

		/**
		 * Get the preview iframe document for a specific PDF settings section.
		 *
		 * @since 1.0.0
		 *
		 * @param {string|null} pdfId The ID of the PDF settings section.
		 *
		 * @return {Document|null} The preview iframe document.
		 */
		getPreviewIframeDoc( pdfId = null ) {
			const $iframe = app.getPreviewIframe( pdfId );

			if ( ! $iframe.length ) {
				return null;
			}

			const doc = $iframe[ 0 ]?.contentWindow.document;

			return doc || null;
		},

		/**
		 * Update the content of the preview container.
		 *
		 * @since 1.0.0
		 *
		 * @param {string} pdfId The ID of the PDF settings section.
		 * @param {string} html  The HTML content to display.
		 */
		updatePreviewIframe( pdfId, html ) {
			const iframeDoc = app.getPreviewIframeDoc( pdfId );

			if ( ! iframeDoc ) {
				return;
			}

			iframeDoc.documentElement.innerHTML = html;
		},

		/**
		 * Show a loading spinner in the preview container.
		 *
		 * @since 1.0.0
		 *
		 * @param {string} pdfId The ID of the PDF settings section.
		 */
		showSpinner( pdfId ) {
			const $iframe = app.getPreviewIframe( pdfId );

			if ( ! $iframe.length || ! $iframe.hasClass( 'active' ) ) {
				return;
			}

			app.el.$previewSpinner.removeClass( 'wpforms-hidden' );
			app.el.$previewZoom.addClass( 'wpforms-disabled' );

			$( '.wpforms-pdf .wpforms-builder-settings-block-content' ).addClass( 'wpforms-disabled' );
			$( '.wpforms-pdf .wpforms-smart-tags-widget-input' ).attr( 'contenteditable', false );

			WPForms.Admin.Builder?.UndoRedo?.preventRun( true );
		},

		/**
		 * Hide a loading spinner in the preview container.
		 *
		 * @since 1.0.0
		 */
		hideSpinner() {
			app.el.$previewSpinner.addClass( 'wpforms-hidden' );
			app.el.$previewZoom.removeClass( 'wpforms-disabled' );

			$( '.wpforms-pdf .wpforms-builder-settings-block-content' ).removeClass( 'wpforms-disabled' );
			$( '.wpforms-pdf .wpforms-smart-tags-widget-input' ).attr( 'contenteditable', true );

			WPForms.Admin.Builder?.UndoRedo?.preventRun( false );
		},

		/**
		 * Update the zoom preview iframe scaling.
		 *
		 * @since 1.0.0
		 *
		 * @param {jQuery} $container The zoom container element.
		 * @param {jQuery} $iframe    The zoom iframe element.
		 */
		updateZoomIframeScaling( $container, $iframe ) {
			$container = $container.length ? $container : $( '.wpforms-pdf-preview-zoom' );
			$iframe = $iframe.length ? $iframe : $( '.wpforms-pdf-preview-zoom iframe' );

			const scale = $container.outerHeight() / ( $iframe.outerHeight() - 1 );

			$iframe[ 0 ].style.transform = `scale(${ scale })`;
		},

		/**
		 * Highlight the preview element.
		 *
		 * @since 1.0.0
		 */
		highLightPreviewElement() {
			const reflectorElement = reflector.getElement();
			app.activatePreviewIframe( reflectorElement.pdfId );

			reflector.highLightContainer( reflectorElement.$previewContainer );
		},

		/**
		 * Show an error in the preview.
		 *
		 * @since 1.0.0
		 *
		 * @param {string} message The error message to display.
		 */
		showError( message ) {
			if ( ! app.el.$previewError ) {
				// language=HTML
				app.el.$previewError = $( '<div id="wpforms-pdf-preview-error" class="wpforms-hidden">' );
				app.el.$preview.append( app.el.$previewError );
			}

			app.el.$previewError
				.text( message )
				.removeClass( 'wpforms-hidden' );

			// Hide the error after 5 seconds.
			setTimeout( () => {
				app.hideError();
			}, 5000 );
		},

		/**
		 * Hide an error in the preview.
		 *
		 * @since 1.0.0
		 */
		hideError() {
			if ( ! app.el.$previewError ) {
				return;
			}

			app.el.$previewError.addClass( 'wpforms-hidden' );
		},

		/**
		 * Fetch the preview HTML via AJAX.
		 *
		 * @since 1.0.0
		 *
		 * @param {string}  pdfId    The ID of the PDF settings section.
		 * @param {boolean} debounce Whether to debounce the request. Default is true.
		 */
		fetchPreview( pdfId, debounce = true ) {
			// Ensure pdfId is valid.
			if ( ! pdfId ) {
				return;
			}

			// Show loading state.
			app.showSpinner( pdfId );

			// Fetch the preview HTML via AJAX.
			if ( debounce ) {
				// Avoid making too many requests at once.
				app.fetchPreviewAjaxDebounced( pdfId );
			} else {
				app.fetchPreviewAjax( pdfId );
			}
		},

		/**
		 * Fetch the preview HTML via AJAX.
		 *
		 * @since 1.0.0
		 *
		 * @param {string} pdfId The ID of the PDF settings section.
		 */
		fetchPreviewAjax( pdfId ) {
			if ( ! pdfId || ! app.el?.$form?.length ) {
				return;
			}

			const theme = $( `#wpforms-panel-field-pdfs-${ pdfId }-theme` ).val();
			const template = $( `#wpforms-panel-field-pdfs-${ pdfId }-template_style` ).val();

			// Do not send request if theme or template is empty since this causes the error.
			// This shouldn't happen in normal circumstances.
			// However, it is possible when changes are applied programmatically.
			if ( ! theme?.length || ! template?.length ) {
				app.hideSpinner();
				return;
			}

			// Prepare data for AJAX request.
			const data = {
				action: 'wpforms_pdf_get_preview',
				nonce: wpforms_builder.nonce,
				form_id: app.formId, // eslint-disable-line camelcase
				pdf_id: pdfId, // eslint-disable-line camelcase
				form_data: JSON.stringify( WPFormsBuilder.serializeAllData( app.el.$form ) ), // eslint-disable-line camelcase
			};

			app.hideError();

			// Generate a unique request ID.
			const requestId = pdfId + '_' + ( new Date().getTime() ) + '_' + Math.random().toString( 36 ).substring( 2, 13 );

			// Add this request to the tracking array.
			app.loadingPreviews.push( requestId );

			// Make the AJAX call.
			$.post( wpforms_builder.ajax_url, data )
				.done( function( response ) {
					if ( ! response.success ) {
						app.showError( wpforms_builder.pdf_preview_error );
						return;
					}

					// Only update the preview if this is the last request for this PDF ID.
					const currentRequests = app.loadingPreviews.filter( function( id ) {
						return id !== requestId && id.startsWith( pdfId + '_' );
					} );

					if ( ! currentRequests.length ) {
						app.initPreviewPaper( pdfId );
						app.updatePreviewIframe( pdfId, response.data.html );
						reflector.clearData( pdfId );
						app.afterUpdatePreview( pdfId );
					}
				} )
				.fail( function( xhr, textStatus ) {
					const errorMessage = wpforms_builder.pdf_preview_error;
					const errorResponse = xhr.responseText || textStatus || '';

					wpf.debug( errorMessage, errorResponse );
					app.showError( errorMessage );
				} )
				.always( function() {
					// Remove this specific request ID from the tracking array.
					app.loadingPreviews = app.loadingPreviews.filter( function( id ) {
						return id !== requestId;
					} );

					if ( ! app.loadingPreviews.length ) {
						app.hideSpinner();
					}
				} );
		},

		/**
		 * After updating the preview.
		 *
		 * @since 1.2.0
		 *
		 * @param {string} pdfId The ID of the PDF settings section.
		 */
		afterUpdatePreview( pdfId ) {
			// Trigger font change event to update the preview.
			$( `#wpforms-panel-field-pdfs-${ pdfId }-general_font, #wpforms-panel-field-pdfs-${ pdfId }-notification_font` ).trigger( 'change' );
			WPFormsPDFBuilder.previewReflector.removeHighLighting();
		},

		// --------------------------------------------------------------------
		// Event Handlers
		// --------------------------------------------------------------------

		/**
		 * Handle `wpformsSettingsBlockAdded`.
		 *
		 * @since 1.0.0
		 *
		 * @param {Object} event  The event object.
		 * @param {jQuery} $block The 'settings block' element.
		 */
		pdfAdded( event, $block ) {
			if ( $block.data( 'block-type' ) !== 'pdf' ) {
				return;
			}

			const pdfId = $block.data( 'block-id' );

			app.addPreviewIframe( $block );
			app.activatePreviewIframe( pdfId );
			app.showSpinner( pdfId );
			app.handleOpenPreviewSidebar( null );
		},

		/**
		 * Handle `wpformsSettingsBlockDeleted`.
		 *
		 * @since 1.0.0
		 *
		 * @param {Object} event     The event object.
		 * @param {string} blockType The deleted block type.
		 * @param {string} blockId   The deleted block ID.
		 */
		handleSettingsBlockDeleted( event, blockType, blockId ) {
			if ( blockType !== 'pdf' ) {
				return;
			}

			app.removePreviewIframe( blockId );

			const $pdfs = app.el.$settings.find( '.wpforms-builder-settings-block' );
			const pdfId = $pdfs.first().data( 'block-id' );

			if ( ! pdfId ) {
				app.handleClosePreviewSidebar( null );
			}

			app.activatePreviewIframe( pdfId );
		},

		/**
		 * Handle opening the preview sidebar.
		 *
		 * @since 1.0.0
		 *
		 * @param {Object} event The event object.
		 */
		handleOpenPreviewSidebar( event ) {
			if ( ! app.getBlocks().length ) {
				return;
			}

			app.el.$previewSidebar?.removeClass( 'wpforms-hidden' );
			event?.preventDefault();
		},

		/**
		 * Handle closing the preview sidebar.
		 *
		 * @since 1.0.0
		 *
		 * @param {Object} event The event object.
		 */
		handleClosePreviewSidebar( event ) {
			app.el.$previewSidebar.addClass( 'wpforms-hidden' );
			event?.preventDefault();
		},

		/**
		 * Handle clicking the zoom button.
		 *
		 * @since 1.0.0
		 *
		 * @param {Object} event The event object.
		 */
		handleRefreshPreview( event ) {
			event?.preventDefault();

			const pdfId = $( '.wpforms-pdf-preview-iframe.active' ).attr( 'id' )
				.replace( 'wpforms-pdf-preview-iframe-', '' );

			app.fetchPreview( pdfId );
			reflector.clearData( pdfId );
		},

		/**
		 * Handle clicking the zoom button.
		 *
		 * @since 1.0.0
		 *
		 * @param {Object} event The event object.
		 */
		handleClickZoomButton( event ) {
			event?.preventDefault();

			const $iframe = app.getPreviewIframe( null ); // Active iframe.
			const orientation = app.el.$preview.attr( 'data-orientation' );
			const paperSize = app.el.$preview.attr( 'data-paper-size' );
			const $zoomIframe = $iframe.clone();
			const html = $iframe[ 0 ]?.contentWindow.document.documentElement.outerHTML;

			$zoomIframe
				.attr( 'id', $zoomIframe.attr( 'id' ) + '-zoom' )
				.attr( 'scrolling', 'no' );

			const $zoomContainer = $zoomIframe.wrap( '<div class="wpforms-pdf-preview-zoom"></div>' ).parent();

			// Open the modal.
			$.confirm( {
				title: false,
				content: $zoomContainer,
				icon: false,
				type: 'pdf-preview',
				theme: 'modern wpforms-pdf-preview-zoom',
				closeIcon: true,
				buttons: false,
				escapeKey: true,
				backgroundDismiss: true,
				boxWidth: 'fit-content',
				onOpenBefore() {
					$zoomContainer
						.closest( '.jconfirm-content' )
						.attr( 'data-orientation', orientation )
						.attr( 'data-paper-size', paperSize );

					app.updateZoomIframeScaling( $zoomContainer, $zoomIframe );

					// Update the zoom iframe document on load.
					$zoomIframe.on( 'load', () => {
						const doc = $zoomIframe[ 0 ]?.contentWindow.document;

						if ( doc ) {
							doc.documentElement.innerHTML = html;
						}
					} );
				},
				onOpen() {
					this.$closeIcon.addClass( 'show' );

					// Update the zoom iframe scaling on the window resize.
					$( window ).on( 'resize.pdfPreviewZoom', _.debounce( () => {
						app.updateZoomIframeScaling( $zoomContainer, $zoomIframe );
					}, 100 ) );
				},
				onClose() {
					$( window ).off( 'resize.pdfPreviewZoom' );
				},
			} );
		},

		/**
		 * Handle `wpformsPDFTemplateStyleChange`.
		 *
		 * @since 1.0.0
		 *
		 * @param {Object} event The event object.
		 * @param {string} pdfId The PDF block ID.
		 */
		handleTemplateStyleChange( event, pdfId ) {
			app.activatePreviewIframe( pdfId );
			app.showSpinner( pdfId );
		},

		/**
		 * Handle `wpformsPDFTemplateStyleChange`.
		 *
		 * @since 1.0.0
		 *
		 * @param {Object} event The event object.
		 * @param {string} pdfId The PDF block ID.
		 */
		handleTemplateStyleChanged( event, pdfId ) {
			app.activatePreviewIframe( pdfId );
			app.fetchPreview( pdfId );
		},

		/**
		 * Handle clicks on the preview container.
		 *
		 * @since 1.0.0
		 *
		 * @param {Object} event The event object.
		 */
		handlePreviewClick( event ) {
			// Prevent default if the click was on a link/button inside potentially.
			event.preventDefault();
			event.stopPropagation();
		},

		/**
		 * Handle setting focus.
		 *
		 * @since 1.0.0
		 */
		handleSettingFocus() {
			const $input = $( this );

			if ( ! reflector.addElement( $input ) ) {
				return;
			}

			app.el.$previewSidebar?.removeClass( 'wpforms-hidden' );
			app.highLightPreviewElement();
		},

		/**
		 * Handle setting blur.
		 *
		 * @since 1.0.0
		 */
		handleSettingBlur() {
			reflector.removeHighLighting();
		},

		/**
		 * Handle setting input.
		 *
		 * @since 1.0.0
		 *
		 * @param {Object} e       The event object.
		 * @param {string} context The context.
		 */
		handleSettingInput( e, context ) {
			const $input = $( this );

			app.updateElement( $input, context );
		},

		/**
		 * Update the reflector element.
		 *
		 * @since 1.0.0
		 *
		 * @param {jQuery}  $input    Input element.
		 * @param {string}  context   The context.
		 * @param {boolean} highlight Whether to highlight the element.
		 */
		updateElement( $input, context = '', highlight = true ) {
			if ( ! reflector.addElement( $input ) ) {
				return;
			}

			reflector.updateElement( $input, context );

			if ( highlight && context !== 'triggered' ) {
				app.highLightPreviewElement();
			}
		},

		/**
		 * Handle the `wpformsImageUploadChange` event.
		 *
		 * @since 1.0.0
		 *
		 * @param {Object} event    The event object.
		 * @param {jQuery} $control The control element.
		 */
		handleImageUploadChange( event, $control ) {
			const $input = $control.find( '.wpforms-image-upload-url' );

			if ( ! reflector.addElement( $input ) ) {
				return;
			}

			reflector.updateElement( $input );
			app.highLightPreviewElement();
		},

		/**
		 * Handle image remove button mousedown (before actual remove).
		 *
		 * @since 1.0.0
		 */
		handleBeforeImageRemove() {
			const $input = $( this ).closest( '.wpforms-setting-field-image-upload' ).find( '.wpforms-image-upload-url' );

			if ( ! reflector.addElement( $input ) ) {
				return;
			}

			app.el.$previewSidebar?.removeClass( 'wpforms-hidden' );
			app.highLightPreviewElement();
		},
	};

	// Provide access to public functions/properties.
	return app;
}