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/www/wp-content/plugins/wpforms-pdf/assets/js/modules/templates.js
/* global wpformsPDF, WPFormsPDFBuilder, WPFormsBuilder, wpforms_builder, WPForms, WPFormsUtils */

/**
 * @param wpforms_builder.pdf_server_error
 * @param wpforms_builder.pdf.settings.loading_error
 * @param wpforms_builder.pdf.template.reset_warning
 * @param wpforms_builder.pdf_text
 * @param wpformsPDF.categories.default_style
 * @param wpformsPDF.tinymce_defaults
 * @param wpformsPDF.text_settings
 */

/**
 * WPForms PDF: Templates 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
	/**
	 * Elements holder.
	 *
	 * @since 1.0.0
	 *
	 * @type {Object}
	 */
	const el = {};

	/**
	 * Variables holder.
	 *
	 * @since 1.0.0
	 *
	 * @type {Object}
	 */
	const vars = {
		wpformsPdfTextSettingsChanged: false,
		userSelectedTemplate: false,
	};

	/**
	 * Public functions and properties.
	 *
	 * @since 1.0.0
	 */
	const app = {
		/**
		 * Start the engine.
		 *
		 * @since 1.0.0
		 */
		init() {
			app.ready();
		},

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

		/**
		 * Setup. Prepare some variables.
		 *
		 * @since 1.0.0
		 */
		setup() {
			// Cache DOM elements.
			el.$builder = $( '#wpforms-builder' );
			el.$form = $( '#wpforms-builder-form' );
			el.$document = $( document );
			el.$richtextFields = $( '.wpforms-pdf-tinymce-smarttags' );
		},

		/**
		 * Bind events.
		 *
		 * @since 1.0.0
		 */
		bindEvents() {
			el.$builder
				.on( 'wpformsBuilderReady', app.builderReady )
				.on( 'wpformsSettingsBlockAdded', app.pdfAdded )
				.on( 'wpformsSettingsBlockCloned', app.pdfCloned )
				.on( 'change', '.wpforms-pdf-template-category select', app.handleTemplatesCategoryChange )
				.on( 'change', '.wpforms-pdf-template-style select', app.handleTemplateStyleChange )
				.on( 'change', 'input[id*="-badge_show"]', app.handleBadgeToggleChange )
				.on( 'change', 'input[id*="-signature_show"]', app.handleSignatureToggleChange )
				.on( 'change', 'select[id*="-signature_type"]', app.handleSignatureTypeChange )
				.on( 'click', '.wpforms-pdf-all-templates-modal', app.handleOnOpenModal )
				.on( 'change', '.wpforms-pdf-text-settings select', app.handlePdfTextSettingsChange )
				.on( 'blur', '.wpforms-pdf-text-settings input', app.handlePdfTextSettingsChange )
				.on( 'click', '.wpforms-pdf-text-settings button', app.handlePdfTextSettingsChange );

			el.$document
				.on( 'change', '.wpforms-pdf-view-all-templates-modal-content input[type="radio"]', app.handleOnChangeTemplate );
		},

		/**
		 * Builder is ready.
		 *
		 * @since 1.3.0
		 */
		builderReady() {
			// Initialize TinyMCE for all rich text fields.
			app.initTinyMCE( el.$richtextFields );
		},

		/**
		 * The PDF block was added.
		 *
		 * @since 1.0.0
		 *
		 * @param {Event}  e      The event object.
		 * @param {jQuery} $block The block element.
		 */
		pdfAdded( e, $block ) {
			if ( $block.data( 'block-type' ) !== 'pdf' ) {
				return;
			}

			app.regenerateTemplatesStyleDropdown( $block.find( '.wpforms-pdf-template-category select' ) );
			app.templateStyleChange( $block.find( '.wpforms-pdf-template-style select' ) );
			app.initializeConditionalsForBlock( $block );
		},

		/**
		 * The PDF block was cloned.
		 *
		 * @since 1.0.0
		 *
		 * @param {Event}  e      The event object.
		 * @param {jQuery} $block The block element.
		 */
		pdfCloned( e, $block ) {
			if ( $block.data( 'block-type' ) !== 'pdf' ) {
				return;
			}

			// Re-init TinyMCE editors.
			$block.find( '.wpforms-pdf-tinymce-smarttags' ).each( function() {
				const $textarea = $( this );
				const $editor = $textarea.closest( '.wp-editor-wrap' );

				$textarea.detach().attr( 'style', null );
				$editor.replaceWith( $textarea );
				app.initTinyMCE( $textarea );
			} );
		},

		/**
		 * Initialize conditionals for all existing elements on the page.
		 *
		 * @since 1.0.0
		 */
		initializeExistingConditionals() {
			el.$builder.find( '.wpforms-pdf.wpforms-builder-settings-block' ).each( function() {
				app.initializeConditionalsForBlock( $( this ) );
			} );
		},

		/**
		 * Initialize conditionals for elements within a specific block.
		 *
		 * @since 1.0.0
		 * @param {jQuery} $block The block element.
		 */
		initializeConditionalsForBlock( $block ) {
			// Find relevant controls within the specific block and apply logic.
			$block.find( 'input[id*="-badge_show"]' ).each( function() {
				app.applyBadgeConditionalLogic( $( this ) );
			} );
			$block.find( 'input[id*="-signature_show"]' ).each( function() {
				app.applySignatureVisibilityLogic( $( this ) );
			} );
			$block.find( 'select[id*="-signature_type"]' ).each( function() {
				app.applySignatureTypeLogic( $( this ) );
			} );
		},

		/**
		 * Handle the "change" event for the Show Badge toggle.
		 *
		 * @since 1.0.0
		 */
		handleBadgeToggleChange() {
			app.applyBadgeConditionalLogic( $( this ) );
		},

		/**
		 * Handle the "change" event for the Show Signature toggle.
		 *
		 * @since 1.0.0
		 */
		handleSignatureToggleChange() {
			app.applySignatureVisibilityLogic( $( this ) );
		},

		/**
		 * Handle the "change" event for the Show Signature toggle.
		 *
		 * @since 1.0.0
		 */
		handleSignatureTypeChange() {
			app.applySignatureTypeLogic( $( this ) );
		},

		/**
		 * Apply conditional logic for badge fields based on the toggle state.
		 *
		 * @since 1.0.0
		 * @param {jQuery} $badgeToggle The badge toggle checkbox element.
		 */
		applyBadgeConditionalLogic( $badgeToggle ) {
			const toggleId = $badgeToggle.attr( 'id' );
			const pdfId = toggleId.match( /wpforms-panel-field-pdfs-(\d+)-badge_show/ )?.[ 1 ];

			if ( ! pdfId ) {
				return;
			}

			// Find related fields within the same settings block.
			const $blockContent = $badgeToggle.closest( '.wpforms-builder-settings-block-content' );
			const $badgeFields = $blockContent.find( `[id*="wpforms-panel-field-pdfs-${ pdfId }-badge_year-wrap"], [id*="wpforms-panel-field-pdfs-${ pdfId }-badge_year_color-wrap"], [id*="wpforms-panel-field-pdfs-${ pdfId }-badge_subheading-wrap"], [id*="wpforms-panel-field-pdfs-${ pdfId }-badge_subheading_color-wrap"]` );

			if ( $badgeToggle.is( ':checked' ) ) {
				$badgeFields.show();
			} else {
				$badgeFields.hide();
			}
		},

		/**
		 * Apply conditional logic for signature visibility based on the toggle state.
		 *
		 * @since 1.0.0
		 * @param {jQuery} $signatureToggle The signature toggle checkbox element.
		 */
		applySignatureVisibilityLogic( $signatureToggle ) {
			const toggleId = $signatureToggle.attr( 'id' );
			const pdfId = toggleId.match( /wpforms-panel-field-pdfs-(\d+)-signature_show/ )?.[ 1 ];

			if ( ! pdfId ) {
				return;
			}

			// Find related fields within the same settings block.
			const $blockContent = $signatureToggle.closest( '.wpforms-builder-settings-block-content' );
			const $signatureFields = $blockContent.find( `[id*="wpforms-panel-field-pdfs-${ pdfId }-signature_type-wrap"], [id*="wpforms-panel-field-pdfs-${ pdfId }-signature_text-wrap"], [id*="wpforms-panel-field-pdfs-${ pdfId }-signature_text_color-wrap"], [id*="wpforms-panel-field-pdfs-${ pdfId }-signature-wrap"], [id*="wpforms-panel-field-pdfs-${ pdfId }-signature_subheading-wrap"], [id*="wpforms-panel-field-pdfs-${ pdfId }-signature_subheading_color-wrap"]` );
			const $signatureTypeSelect = $blockContent.find( `select[id*="wpforms-panel-field-pdfs-${ pdfId }-signature_type"]` );

			if ( $signatureToggle.is( ':checked' ) ) {
				$signatureFields.show();
				// Ensure the type logic is also applied when visibility is turned on.
				if ( $signatureTypeSelect.length ) {
					app.applySignatureTypeLogic( $signatureTypeSelect );
				}
			} else {
				$signatureFields.hide();
			}
		},

		/**
		 * Apply conditional logic for signature type (text/image) based on the select value.
		 *
		 * @since 1.0.0
		 * @param {jQuery} $signatureTypeSelect The signature type select element.
		 */
		applySignatureTypeLogic( $signatureTypeSelect ) {
			const selectId = $signatureTypeSelect.attr( 'id' ); // Changed from toggleId to selectId.
			const pdfId = selectId.match( /wpforms-panel-field-pdfs-(\d+)-signature_type/ )?.[ 1 ];

			if ( ! pdfId ) {
				return;
			}

			// Find related fields and the visibility toggle within the same settings block.
			const $blockContent = $signatureTypeSelect.closest( '.wpforms-builder-settings-block-content' );
			const $textFields = $blockContent.find( `[id*="wpforms-panel-field-pdfs-${ pdfId }-signature_text-wrap"], [id*="wpforms-panel-field-pdfs-${ pdfId }-signature_text_color-wrap"]` );
			const $imageFields = $blockContent.find( `[id*="wpforms-panel-field-pdfs-${ pdfId }-signature-wrap"]` );
			const $signatureToggle = $blockContent.find( `input[id*="wpforms-panel-field-pdfs-${ pdfId }-signature_show"]` );

			// Only apply type logic if the main signature visibility is enabled.
			if ( $signatureToggle.length && ! $signatureToggle.is( ':checked' ) ) {
				$textFields.hide();
				$imageFields.hide();
				return; // Stop if a signature section is hidden.
			}

			const selectedType = $signatureTypeSelect.val();

			if ( selectedType === 'text' ) {
				$textFields.show();
				$imageFields.hide();
			} else if ( selectedType === 'image' ) {
				$textFields.hide();
				$imageFields.show();
			} else {
				// Default case or if type is neither text nor image (optional).
				$textFields.hide();
				$imageFields.hide();
			}
		},

		/**
		 * Handle the "click" event for opening the modal.
		 * This will open the modal with the available templates.
		 *
		 * @since 1.0.0
		 *
		 * @param {Event} event The event object.
		 */
		handleOnOpenModal( event ) {
			event.preventDefault();

			const template = wp.template( 'wpforms-pdf-view-all-templates-modal' );

			// If the template doesn't exist, exit the function.
			if ( ! template.length ) {
				return;
			}

			const $link = $( this );
			const $block = $link.closest( '.wpforms-pdf.wpforms-builder-settings-block' );

			// The wrapper element for the clicked link and template settings.
			const $wrapper = $link.closest( '.wpforms-panel-fields-group.wpforms-pdf-template-group' );

			// The 'style select' element.
			const $styleSelect = $wrapper.find( '.wpforms-pdf-template-style select' );

			// Get the selected value from the category select element and its ID.
			const selectedCategory = $styleSelect.val();

			const templatesByCategories = app.getTemplatesByCategories();

			// The PDF ID.
			const pdfId = $block.data( 'block-id' );
			const backgroundUrlPath = wpformsPDF.pluginUrl + 'assets/images/modal-previews/';
			const version = wpformsPDF.version;
			const data = { templatesByCategories, selectedCategory, pdfId, backgroundUrlPath, version };
			const content = template( data );

			vars.modal = $.confirm( {
				content,
				title: '',
				boxWidth: 'calc( 100vw - 80px )',
				backgroundDismiss: true,
				smoothContent: false,
				closeIcon: true,
				closeIconClass: 'wpforms-pdf-templates-modal-close-icon',
				buttons: false,
				onOpen() {
					$( '.wpforms-pdf-view-all-templates-modal-content.wpforms-modal-content' )
						.toggleClass(
							'wpforms-scrollbar-compact',
							! navigator.userAgent.includes( 'Macintosh' ) && ! navigator.userAgent.includes( 'Firefox' )
						)
						.scrollTop( 0 );
				},
				onDestroy: app.onDestroyTemplatesModal,
			} );
		},

		/**
		 * The `onDestroy` event for closing the modal.
		 *
		 * @since 1.0.0
		 */
		onDestroyTemplatesModal() {
			if ( ! vars.userSelectedTemplate ) {
				vars.userSelectedTemplate = false;
				return;
			}

			if ( ! vars.wpformsPdfTextSettingsChanged ) {
				app.afterClosingTemplatesModal();
				return;
			}

			$.confirm( {
				title: wpforms_builder.heads_up,
				content: wpforms_builder.pdf.template.reset_warning,
				icon: 'fa fa-exclamation-circle',
				type: 'orange',
				buttons: {
					confirm: {
						text: wpforms_builder.ok,
						btnClass: 'btn-confirm',
						action() {
							app.afterClosingTemplatesModal();
						},
					},
					cancel: {
						text: wpforms_builder.cancel,
						btnClass: 'wpforms-btn wpforms-btn-secondary',
					},
				},
			} );

			vars.userSelectedTemplate = false;
		},

		/**
		 * Get templates by categories.
		 *
		 * @since 1.0.0
		 *
		 * @return {Array} An array of templates by categories.
		 */
		getTemplatesByCategories() {
			const templates = wpformsPDF.templates || {};
			const categoriesLocalized = wpformsPDF.categories || [];
			// Each template has a category property.
			const categories = Object.values( templates ).map( ( template ) => template.category );
			// Remove duplicates.
			const uniqueCategories = [ ...new Set( categories ) ];

			return uniqueCategories.map( ( category ) => {
				const filteredTemplates = Object.values( templates ).filter( ( template ) => template.category === category );

				// If there are 4 or 5 templates, fill it up to 6.
				while ( filteredTemplates.length > 3 && filteredTemplates.length < 6 ) {
					filteredTemplates.push( {} );
				}

				return {
					category,
					categoryLocalized: categoriesLocalized[ category ],
					templates: filteredTemplates,
				};
			} );
		},

		/**
		 * Handle the "change" event for the template radio buttons.
		 *
		 * @since 1.0.0
		 *
		 * @param {Event} event The event object.
		 */
		handleOnChangeTemplate( event ) {
			event.preventDefault();

			const $this = $( this ),
				pdfId = $this.data( 'pdf-id' );

			vars.userSelectedTemplate = true;
			vars.style = $this.val();
			vars.category = $this.data( 'category' );
			vars.$categorySelect = $( `select#wpforms-panel-field-pdfs-${ pdfId }-template_category` );
			vars.$styleSelect = $( `select#wpforms-panel-field-pdfs-${ pdfId }-template_style` );

			vars.modal.close();
		},

		/**
		 * Handle the "close" event for the template modal.
		 *
		 * @since 1.0.0
		 */
		afterClosingTemplatesModal() {
			if ( vars.category !== vars.$categorySelect.val() ) {
				vars.$categorySelect.val( vars.category );
				app.regenerateTemplatesStyleDropdown( vars.$categorySelect );
				WPFormsPDFBuilder.appearance.toggleAppearanceSettingsVisibility( vars.$categorySelect );
			}

			vars.$styleSelect.val( vars.style );
			app.templateStyleChange( vars.$styleSelect );
			WPFormsPDFBuilder.custom.toggleCustomEntityVisibility( {}, vars.$styleSelect );

			vars.wpformsPdfTextSettingsChanged = false;
		},

		/**
		 * Handle PDF text settings change.
		 *
		 * @since 1.0.0
		 */
		handlePdfTextSettingsChange() {
			vars.wpformsPdfTextSettingsChanged = true;
		},

		/**
		 * Handle template style change.
		 *
		 * @since 1.0.0
		 */
		handleTemplateStyleChange() {
			app.templateStyleChange( $( this ) );
		},

		/**
		 * Process template style change logic.
		 *
		 * @since 1.0.0
		 *
		 * @param {jQuery} $styleSelect The select element that changed.
		 */
		templateStyleChange( $styleSelect ) {
			const $blockContent = $styleSelect.closest( '.wpforms-builder-settings-block-content' );
			const $textSettings = $blockContent.find( '.wpforms-pdf-text-settings' );
			const $templateCategorySelect = $blockContent.find( '.wpforms-pdf-template-category select' );
			const selectedTemplateCategory = $templateCategorySelect.val();
			const pdfId = $blockContent.closest( '.wpforms-pdf' ).data( 'block-id' );
			const $themeSelect = $blockContent.find( '.wpforms-field-pdf-theme select' );
			const selectedStyle = $styleSelect.val();
			const selectedTheme = $themeSelect.val();

			// Trigger custom event before fetching template settings.
			WPFormsUtils.triggerEvent( el.$builder, 'wpformsPDFTemplateStyleChange', [ pdfId, selectedStyle, selectedTheme ] );

			// Apply theme changes.
			WPFormsPDFBuilder.appearance.applyThemeChanges( $themeSelect );

			if ( selectedTemplateCategory === 'notification' ) {
				$textSettings.html( '' );
				WPFormsUtils.triggerEvent( el.$builder, 'wpformsPDFTemplateStyleChanged', [ pdfId, selectedStyle, selectedTheme ] );

				return;
			}

			// Ensure $textSettings exists before proceeding.
			if ( ! $textSettings.length ) {
				return;
			}

			$textSettings.css( 'opacity', '0.5' );

			// Fetch template settings via AJAX.
			app.fetchTemplateSettings( pdfId, selectedStyle, selectedTheme, $textSettings );
		},

		/**
		 * Fetch template settings via AJAX.
		 *
		 * @since 1.0.0
		 *
		 * @param {string} pdfId         PDF ID.
		 * @param {string} selectedStyle Selected template style.
		 * @param {string} selectedTheme Selected theme.
		 * @param {jQuery} $textSettings Text settings container.
		 */
		fetchTemplateSettings( pdfId, selectedStyle, selectedTheme, $textSettings ) {
			// Validate required parameters.
			if ( ! pdfId || ! $textSettings.length ) {
				return;
			}

			// Prepare request data.
			/* eslint-disable camelcase */
			const requestData = {
				action: 'wpforms_pdf_refresh_template_fields',
				template_style: selectedStyle || '',
				theme: selectedTheme || '',
				pdf_id: pdfId,
				form_id: el.$form.data( 'id' ),
				form_data: JSON.stringify( WPFormsBuilder.serializeAllData( el.$form ) ),
				nonce: wpforms_builder.nonce,
			};
			/* eslint-enable camelcase */

			// Make AJAX request to get updated template settings fields.
			$.ajax( {
				url: wpforms_builder.ajax_url,
				type: 'POST',
				data: requestData,
				success( response ) {
					app.handleTemplateSettingsResponse( response, $textSettings );
				},
				error() {
					app.displayTemplateSettingsError( $textSettings, true );
				},
				complete() {
					$textSettings.css( 'opacity', '1' );
					WPFormsUtils.triggerEvent( el.$builder, 'wpformsPDFTemplateStyleChanged', [ pdfId, selectedStyle, selectedTheme ] );
				},
			} );
		},

		/**
		 * Handle template settings AJAX response.
		 *
		 * @since 1.0.0
		 *
		 * @param {Object} response      AJAX response.
		 * @param {jQuery} $textSettings Text settings container.
		 */
		handleTemplateSettingsResponse( response, $textSettings ) {
			// Handle successful response with HTML content.
			if ( response.success && response.data?.html ) {
				// Replace current settings with new HTML.
				$textSettings.html( response.data.html );

				// Reinitialize necessary components.
				app.initializeTemplateComponents( $textSettings );

				// Re-apply conditional logic if settings include conditional fields.
				app.initializeConditionalsForBlock( $textSettings ); // Apply to newly loaded settings.

				return;
			}

			// Handle successful response but no HTML content.
			if ( response.success && ( ! response.data?.html ) ) {
				$textSettings.html( '' );
				return;
			}

			// Handle unsuccessful response.
			app.displayTemplateSettingsError( $textSettings, false );
		},

		/**
		 * Initialize template components.
		 *
		 * @since 1.0.0
		 *
		 * @param {jQuery} $container Container with components to initialize.
		 */
		initializeTemplateComponents( $container ) {
			app.initTinyMCE( $container.find( '.wpforms-pdf-tinymce-smarttags' ) );
			WPForms.Admin.Builder.SmartTags.initWidgets( $container );
			WPFormsPDFBuilder.appearance.loadColorPickers( $container );

			// Handle TinyMCE change event.
			$container.find( '.wpforms-pdf-tinymce-smarttags' ).each( function() {
				const editorId = $( this ).attr( 'id' );
				if ( window.tinymce && window.tinymce.get( editorId ) ) {
					window.tinymce.get( editorId ).on( 'change', function() {
						app.handlePdfTextSettingsChange();
					} );
				}
			} );
		},

		/**
		 * Display template settings error message.
		 *
		 * @since 1.0.0
		 *
		 * @param {jQuery}  $container    Container to display error in.
		 * @param {boolean} isServerError Whether it's a server error.
		 */
		displayTemplateSettingsError( $container, isServerError ) {
			const alertClass = isServerError ? 'wpforms-alert-danger' : 'wpforms-alert-warning';
			const message = isServerError ? wpforms_builder.pdf_server_error : wpforms_builder.pdf.settings.loading_error;

			$container.html( `<div class="wpforms-alert ${ alertClass }">${ message }</div>` );
		},

		/**
		 * Initialize TinyMCE.
		 *
		 * @since 1.0.0
		 *
		 * @param {jQuery} $elements Elements.
		 */
		initTinyMCE( $elements ) {
			const settings = { ...wpformsPDF.tinymce_defaults };

			settings.tinymce.setup = function( editor ) {
				editor.on( 'init', function() {
					// Replace Code tab label with Text label, translated.
					$( '.wpforms-pdf-text-settings .wp-editor-tabs .switch-html' ).each( ( index, tab ) => {
						tab.textContent = wpforms_builder.pdf_text;
					} );

					// Force display both toolbars.
					$( `#${ editor.id }-wrap .mce-toolbar` ).show();
				} );
			};

			WPFormsBuilder.initElementsTinyMCE( $elements, settings );
		},

		/**
		 * Regenerate templates style dropdown.
		 *
		 * @since 1.0.0
		 *
		 * @param {jQuery} $categorySelect 'Select' element.
		 */
		regenerateTemplatesStyleDropdown( $categorySelect ) {
			const $parentField = $categorySelect.closest( '.wpforms-panel-field' );
			const $styleField = $parentField.siblings( '.wpforms-pdf-template-style' );
			const $styleSelect = $styleField.find( 'select' );
			const selectedCategory = $categorySelect.val();
			const templates = app.getAllTemplates();

			// Clear existing options.
			$styleSelect.empty();

			// Exit if no category selected or templates exists.
			if ( ! selectedCategory || $.isEmptyObject( templates ) ) {
				return;
			}

			const notificationStyle = app.getNotificationDefaultStyle( $categorySelect.closest( '.wpforms-pdf' ) );

			$.each( templates, function( templateKey, template ) {
				if ( template.category !== selectedCategory ) {
					return;
				}

				const defaultStyle = selectedCategory === 'notification'
					? notificationStyle
					: wpformsPDF.categories?.[ selectedCategory ]?.default_style;

				// language=HTML
				const $option = $( '<option></option>' )
					.attr( 'value', templateKey )
					.prop( 'selected', defaultStyle === templateKey )
					.text( template.title );

				$styleSelect.append( $option );
			} );

			app.templateStyleChange( $styleSelect );
		},

		/**
		 * Get notification default style.
		 *
		 * @since 1.0.0
		 *
		 * @param {jQuery} $block 'Block' element.
		 *
		 * @return {string} Notification default style.
		 */
		getNotificationDefaultStyle( $block ) {
			const notificationsJson = $block.find( 'input.wpforms-panel-field-pdf-notifications-json' ).val();
			const firstNotificationId = JSON.parse( notificationsJson )?.[ 0 ];

			if ( ! firstNotificationId ) {
				return wpformsPDF.categories.notification.default_style;
			}

			const notificationStyle = $( `select[name="settings[notifications][${ firstNotificationId }][template]"]` ).val();

			return notificationStyle.length && notificationStyle !== 'none'
				? ( 'notification-' + notificationStyle )
				: wpformsPDF.categories.notification.default_style;
		},

		/**
		 * Handle the template category change.
		 *
		 * @since 1.0.0
		 */
		handleTemplatesCategoryChange() {
			const $select = $( this );

			app.regenerateTemplatesStyleDropdown( $select );
		},

		/**
		 * Template appearance change.
		 *
		 * @since 1.0.0
		 *
		 * @param {jQuery} $select 'Select' element.
		 */
		templateAppearanceChange( $select ) {
			const $blockContent = $select.closest( '.wpforms-builder-settings-block-content' );
			const $templateStyleSelect = $blockContent.find( '.wpforms-pdf-template-style select' );
			const templateData = app.getTemplateData( $templateStyleSelect.val() ) ?? {};

			// Skip if no valid template data.
			if ( $.isEmptyObject( templateData ) || ! templateData.appearance ) {
				return;
			}

			// Update non-color appearance settings.
			// eslint-disable-next-line complexity
			$.each( templateData.appearance, function( option, value ) {
				// Skip color settings (already handled by theme selector)
				if ( typeof value === 'string' && value.includes( '{color_' ) ) {
					return;
				}

				WPFormsPDFBuilder.appearance.applyThemeNonColorValue( option, value, null, templateData, $blockContent );
			} );
		},

		/**
		 * Get all templates data.
		 *
		 * @since 1.0.0
		 *
		 * @return {Object} Templates data.
		 */
		getAllTemplates() {
			return {
				...wpformsPDF.customTemplates,
				...wpformsPDF.templates,
			};
		},

		/**
		 * Get the template data.
		 *
		 * @since 1.0.0
		 *
		 * @param {string} slug Template slug.
		 *
		 * @return {Object|null} Template data.
		 */
		getTemplateData( slug ) { // eslint-disable-line complexity
			const wpformsTemplateData = wpformsPDF.templates[ slug ] ?? null;
			const customTemplateData = wpformsPDF.customTemplates[ slug ] ?? null;
			const templateData = customTemplateData || wpformsTemplateData;

			if ( ! templateData ) {
				return null;
			}

			templateData.isCustom = Boolean( customTemplateData );

			if ( ! templateData.isCustom ) {
				return templateData;
			}

			// Inherit the logo template from the base template.
			const baseTemplateData = wpformsPDF.templates[ templateData.baseTemplate ] ?? null;
			templateData.appearance.logo_template = baseTemplateData?.appearance?.logo_template; // eslint-disable-line camelcase

			return templateData;
		},
	};

	// Return the public facing methods.
	return app;
}