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/appearance.js
/* global wpforms_builder_settings, wpformsPDF, WPFormsPDFBuilder, WPFormsBuilder, wpforms_builder, WPForms */

/**
 * @param config.itemSelectText
 * @param themeData.colors
 * @param wpformsPDF.defaultTheme
 * @param wpformsPDF.pluginUrl
 * @param wpforms_builder.pdf_settings_theme_no_choices
 * @param wpforms_builder.pdf_settings_theme_no_results
 * @param themeData.appearance.logo_template
 * @param themeData.emailAppearance
 */

// noinspection JSUnusedGlobalSymbols, JSUnusedLocalSymbols

/**
 * WPForms PDF: Appearance 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 = {};

	/**
	 * 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.initExistingThemeSelectors();
			app.applyExistingAppearanceLogic();
		},

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

		/**
		 * Bind events.
		 *
		 * @since 1.0.0
		 */
		bindEvents() {
			el.$builder
				.on( 'wpformsSettingsBlockAdded', app.pdfAdded )
				.on( 'wpformsSettingsBlockCloned', app.pdfCloned )
				.on( 'wpformsUndoRedoRun', app.undoRedoRun )
				.on( 'wpformsPDFReflectorElementUpdate', app.themeChanged )
				.on( 'mousedown', '.wpforms-panel-content-section-pdf .minicolors-swatch', app.clickMinicolorsSwatch )
				.on( 'change', '.wpforms-pdf-page-background-image select', app.backgroundImageChanged )
				.on( 'change', '.wpforms-pdf-border-style select', app.borderStyleChanged )
				.on( 'change', '.wpforms-pdf-template-category select', app.templateCategoryChanged );
		},

		/**
		 * 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.initThemeSelectorsForBlock( $block );
			app.applyAppearanceLogicForBlock( $block );
		},

		/**
		 * The PDF block was added.
		 *
		 * @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;
			}

			app.reInitThemeSelectorsForBlock( $block );
			app.applyAppearanceLogicForBlock( $block );
		},

		/**
		 * The undo/redo action was run.
		 *
		 * @since 1.2.0
		 *
		 * @param {Event}  e           The event object.
		 * @param {string} commandType The command type.
		 * @param {Object} command     The command object.
		 */
		undoRedoRun( e, commandType, command ) {
			if ( ! WPFormsPDFBuilder.notifications.shouldProcessUndoRedoEvent( command.args?.event ?? '' ) ) {
				return;
			}

			app.initExistingThemeSelectors();
		},

		/**
		 * The PDF block was added.
		 *
		 * @since 1.0.0
		 *
		 * @param {Event}  e                The event object.
		 * @param {Object} reflectorElement The block element.
		 */
		themeChanged( e, reflectorElement ) {
			if ( reflectorElement.setting !== 'theme' ) {
				return;
			}

			app.applyThemeChanges( $( `#wpforms-panel-field-pdfs-${ reflectorElement.pdfId }-theme` ) );
		},

		/**
		 * Handle click on the Minicolors swatch.
		 *
		 * @since 1.0.0
		 */
		clickMinicolorsSwatch() {
			const $swatch = $( this );
			const $input = $swatch.closest( '.wpforms-panel-field-colorpicker' ).find( '.minicolors-input' );

			// Focus the input. It fixes the preview highlight sticking.
			setTimeout( () => {
				$input.trigger( 'focus' );
			}, 0 );
		},

		/**
		 * The background image change event handler.
		 *
		 * @since 1.0.0
		 */
		backgroundImageChanged() {
			app.applyBackgroundImageLogic( $( this ) );
		},

		/**
		 * The border style change event handler.
		 *
		 * @since 1.0.0
		 */
		borderStyleChanged() {
			app.applyBorderStyleLogic( $( this ) );
		},

		/**
		 * The template category change event handler.
		 *
		 * @since 1.0.0
		 */
		templateCategoryChanged() {
			app.toggleAppearanceSettingsVisibility( $( this ) );
		},

		/**
		 * Initialize existing theme selectors for all elements on the page.
		 *
		 * @since 1.0.0
		 */
		initExistingThemeSelectors() {
			// Skip if Choices.js is not available.
			if ( typeof window.Choices !== 'function' ) {
				return;
			}

			// Initialize existing theme selectors.
			el.$builder.find( '.wpforms-field-pdf-theme' ).each( function() {
				const $field = $( this );
				app.initChoicesForThemeSelector( $field );
				WPFormsPDFBuilder.custom.toggleCustomEntityVisibility( {}, $field.find( 'select.choices__input' ) );
			} );
		},

		/**
		 * Initialize theme selectors for a specific block.
		 *
		 * @since 1.0.0
		 *
		 * @param {jQuery} $block The block element.
		 */
		reInitThemeSelectorsForBlock( $block ) {
			const pdfId = $block.data( 'block-id' );
			const $themeField = $block.find( '.wpforms-field-pdf-theme' );
			const allThemes = app.getAllThemes();
			const currentTheme = app.getSelectedThemeSlug( $block );
			// language=HTML
			const $select = $( '<select></select>' )
				.attr( 'id', `wpforms-panel-field-pdfs-${ pdfId }-theme` )
				.attr( 'name', `settings[pdfs][${ pdfId }][theme]` );

			// Remove existing choices.
			$themeField.find( '.choices' ).remove();

			Object.entries( allThemes ).forEach( function( [ slug, theme ] ) {
				// language=HTML
				const $option = $( '<option></option>' )
					.val( slug )
					.prop( 'selected', slug === currentTheme )
					.text( theme.title );

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

			$themeField.append( $select );
			app.initThemeSelectorsForBlock( $block );
		},

		/**
		 * Initialize theme selectors for a specific block.
		 *
		 * @since 1.0.0
		 *
		 * @param {jQuery} $block The block element.
		 */
		initThemeSelectorsForBlock( $block ) {
			// Skip if Choices.js is not available.
			if ( typeof window.Choices !== 'function' ) {
				return;
			}

			// Initialize the theme selector in the block.
			app.initChoicesForThemeSelector( $block.find( '.wpforms-field-pdf-theme' ), true );
		},

		/**
		 * Initialize Choices.js for a theme selector element.
		 *
		 * @since 1.0.0
		 *
		 * @param {jQuery}  $selector The selector container element.
		 * @param {boolean} force     Force initialization.
		 */
		initChoicesForThemeSelector( $selector, force = false ) {
			// Skip if Choices.js is not available.
			const $selectElement = $selector.find( 'select' );

			// Skip if already initialized or no select element.
			if ( ! $selectElement.length || ( $selectElement.data( 'choicesjs' ) && ! force ) ) {
				return;
			}

			// Add class to parent container for styling.
			$selector.addClass( 'wpforms-pdf-theme-selector' );

			const choicesjsConfig = {
				noChoicesText: wpforms_builder.pdf_settings_theme_no_choices,
				noResultsText: wpforms_builder.pdf_settings_theme_no_results,
				searchChoices: true,
			};

			WPForms.Admin.Builder.WPFormsChoicesJS.setup(
				$selectElement[ 0 ],
				{
					...wpforms_builder_settings.choicesjs_config,
					...choicesjsConfig,
					callbackOnCreateTemplates( template ) {
						return {
							item( classNames, data ) {
								const colors = app.getThemeColors( data.value );

								return template( `
									<div class="choices__item choices__item--selectable" data-item data-id="${ data.id }" data-value="${ data.value }" ${ data.active ? 'aria-selected="true"' : '' } ${ data.disabled ? 'aria-disabled="true"' : '' }>
										${ data.label }
										<div class="wpforms-theme-color-swatches">${ app.getColorSwatchesHTML( colors ) }</div>
									</div>
								` );
							},
							choice( classNames, data ) {
								const colors = app.getThemeColors( data.value );
								const disabledClass = data.disabled ? 'choices__item--disabled' : '';

								return template( `
									<div class="choices__item choices__item--choice choices__item--selectable ${ disabledClass }" data-select-text="${ this.config.itemSelectText }" data-choice ${ data.disabled ? 'data-choice-disabled aria-disabled="true"' : 'data-choice-selectable' } data-id="${ data.id }" data-value="${ data.value }" ${ data.groupId > 0 ? 'role="treeitem"' : 'role="option"' }>
										${ data.label }
										<div class="wpforms-theme-color-swatches">${ app.getColorSwatchesHTML( colors ) }</div>
									</div>
								` );
							},
						};
					},
				}
			);
		},

		/**
		 * Apply appearance logic to all existing elements.
		 *
		 * @since 1.0.0
		 */
		applyExistingAppearanceLogic() {
			el.$builder.find( '.wpforms-pdf' ).each( function() {
				app.applyAppearanceLogicForBlock( $( this ) );
			} );
		},

		/**
		 * Apply appearance logic to elements within a specific block.
		 *
		 * @since 1.0.0
		 *
		 * @param {jQuery} $block The block element.
		 */
		applyAppearanceLogicForBlock( $block ) {
			// Apply background image logic.
			app.applyBackgroundImageLogic( $block.find( '.wpforms-pdf-page-background-image select' ) );

			// Apply border style logic.
			app.applyBorderStyleLogic( $block.find( '.wpforms-pdf-border-style select' ) );

			// Apply theme changes logic.
			app.applyThemeChanges( $block.find( '.wpforms-field-pdf-theme select' ) );

			// Apply template category changes.
			app.toggleAppearanceSettingsVisibility( $block.find( '.wpforms-pdf-template-category select' ) );
		},

		/**
		 * Apply the background image logic.
		 *
		 * @since 1.0.0
		 *
		 * @param {jQuery} $select The border style 'select' element.
		 */
		applyBackgroundImageLogic( $select ) {
			const selectedImage = $select.val();
			const $blockContent = $select.closest( '.wpforms-builder-settings-block-content' );
			const $gradientEnd = $blockContent.find( '[id*="_page_background_color_end"]' );

			// Images that support gradient.
			const gradientImages = [ 'ribbons', 'triangles', 'waves', 'wings', 'four-corners', 'two-corners' ];

			$gradientEnd.toggleClass( 'wpforms-disabled', ! gradientImages.includes( selectedImage ) );
		},

		/**
		 * Apply border style logic based on the selected style.
		 *
		 * @since 1.0.0
		 *
		 * @param {jQuery} $borderStyleSelect The border style 'select' element.
		 */
		applyBorderStyleLogic( $borderStyleSelect ) {
			const selectedStyle = $borderStyleSelect.val();
			const $blockContent = $borderStyleSelect.closest( '.wpforms-builder-settings-block-content' );
			const $borderSettings = $blockContent.find( '.wpforms-pdf-border-size, .wpforms-pdf-border-color' );

			$borderSettings.toggleClass( 'wpforms-disabled', selectedStyle === 'none' );
		},

		/**
		 * Get the data required to applyThemeChanges.
		 *
		 * @since 1.0.0
		 *
		 * @param {jQuery} $themeSelect The theme 'select' element.
		 *
		 * @return {Object|null} Data object or null.
		 */
		getApplyThemeChangesData( $themeSelect ) { // eslint-disable-line complexity
			const selectedTheme = $themeSelect.val();

			// Get theme colors.
			const themeData = app.getThemeData( selectedTheme );

			if ( ! themeData ) {
				return null;
			}

			const themeColors = themeData.colors || {};
			const themeSwatches = Object.values( themeColors );

			if ( ! themeSwatches.length ) {
				return null;
			}

			const $blockContent = $themeSelect.closest( '.wpforms-builder-settings-block-content' );
			const $templateStyleSelect = $blockContent.find( '.wpforms-pdf-template-style select' );
			const selectedTemplateStyle = $templateStyleSelect.val();

			if ( ! selectedTemplateStyle ) {
				return null;
			}

			const templateData = WPFormsPDFBuilder.templates.getTemplateData( selectedTemplateStyle );
			const pdfId = $blockContent.closest( '.wpforms-pdf' ).data( 'block-id' );

			// The default theme in Notifications should have appearance colors inherited from the global Email settings.
			if ( templateData.category === 'notification' && selectedTheme === wpformsPDF.defaultTheme ) {
				themeData.appearance = themeData.emailAppearance;
			}

			let appearance = themeData.appearance ?? {};
			appearance = { ...templateData.appearance, ...themeData.appearance };

			return [ templateData, themeData, $blockContent, appearance, pdfId ];
		},

		/**
		 * Apply theme changes to update colors based on the selected theme.
		 *
		 * @since 1.0.0
		 *
		 * @param {jQuery} $themeSelect The theme 'select' element.
		 */
		applyThemeChanges( $themeSelect ) {
			const data = app.getApplyThemeChangesData( $themeSelect );

			if ( ! data ) {
				return;
			}

			const [ templateData, themeData, $blockContent, appearance, pdfId ] = data;

			[ 'appearance', 'text' ].forEach( function( section ) {
				if ( ! templateData[ section ] ) {
					return;
				}

				$.each( templateData[ section ], function( option, value ) { // eslint-disable-line complexity
					if ( typeof value !== 'string' ) {
						return;
					}

					// Apply color values.
					if ( value.includes( '{color_' ) || option.includes( '_color' ) ) {
						app.applyThemeColorValue( option, value, themeData, $blockContent );

					// Custom themes contain also non-color values in an own appearance section.
					} else if ( section === 'appearance' && appearance[ option ] ) {
						app.applyThemeNonColorValue( option, appearance[ option ], themeData, templateData, $blockContent );
					}
				} );
			} );

			// Update theme editor colors.
			WPFormsPDFBuilder.theme.updateEditorColors( pdfId );

			// Update color pickers in the block.
			app.loadColorPickers( $blockContent );

			setTimeout( WPFormsPDFBuilder.previewReflector.removeHighLighting, 0 );

			// Maybe refresh preview.
			const bgImage = $( `#wpforms-panel-field-pdfs-${ pdfId }-page_background_image` ).val();
			app.maybeRefreshPreview( themeData, pdfId, bgImage );
		},

		/**
		 * Maybe refresh preview.
		 *
		 * @since 1.0.0
		 *
		 * @param {Object} themeData Theme data.
		 * @param {string} pdfId     PDF block ID.
		 * @param {string} bgImage   Background image.
		 */
		maybeRefreshPreview( themeData, pdfId, bgImage ) {
			const emptyValues = [ '', 'none', null, undefined ];
			const hasBgImage = ! emptyValues.includes( themeData.appearance?.page_background_image );

			// Refresh the preview only when the theme contains a background image or shadow.
			if ( hasBgImage && bgImage !== themeData.appearance?.page_background_image ) {
				WPFormsPDFBuilder.preview.fetchPreview( pdfId );
			}
		},

		/**
		 * Apply theme changes to update colors based on the selected theme.
		 *
		 * @since 1.0.0
		 *
		 * @param {string} option        Option name.
		 * @param {string} value         Option value.
		 * @param {Object} themeData     Theme data.
		 * @param {jQuery} $blockContent Block content object.
		 */
		applyThemeColorValue( option, value, themeData, $blockContent ) {
			if ( ! $blockContent?.length ) {
				return;
			}

			value = value.replace( 'none', '' );

			// Extract a color key from a placeholder (e.g., {color_primary} -> primary)
			const colorKey = value.replace( '{color_', '' ).replace( '}', '' );

			const themeColor = themeData.colors[ colorKey ] ?? null;
			const textFieldColor = themeData.text?.[ option ];
			const appearanceColor = themeData.appearance?.[ option ];

			const color = textFieldColor ?? ( appearanceColor ?? themeColor );

			// Find and update the matching input,
			const $colorInput = $blockContent.find(
				`[name*="[notification_${ option }]"],
				[name*="[general_${ option }]"],
				[name*="[${ option }]"]`
			);

			$colorInput.each( function() {
				const $eachInput = $( this );

				$eachInput
					.data( 'fallback-color', '' )
					.attr( 'data-fallback-color', '' )
					.val( color );

				WPFormsPDFBuilder.preview.updateElement( $eachInput, 'triggered' );
			} );
		},

		/**
		 * Apply theme changes to update colors based on the selected theme.
		 *
		 * @since 1.0.0
		 *
		 * @param {string}      option        Option name.
		 * @param {string}      value         Option value.
		 * @param {Object|null} themeData     Theme data.
		 * @param {Object|null} templateData  Template data.
		 * @param {jQuery}      $blockContent Block content object.
		 */
		applyThemeNonColorValue( option, value, themeData, templateData, $blockContent ) {
			if ( ! $blockContent?.length ) {
				return;
			}

			themeData = themeData ?? app.getThemeData( app.getSelectedThemeSlug( $blockContent ) );
			value = value ?? '';

			const $input = $blockContent.find(
				`[name*="[notification_${ option }]"],
				[name*="[general_${ option }]"],
				[name*="[${ option }]"]`
			);

			$input.each( function() {
				const $eachInput = $( this );

				$eachInput.val( value );
				WPFormsPDFBuilder.preview.updateElement( $eachInput, 'triggered' );
				app.applyAppearanceLogicForInput( $eachInput, option, value, themeData, templateData );
			} );
		},

		/**
		 * Apply appearance logic to a given input element.
		 *
		 * @since 1.0.0
		 *
		 * @param {jQuery} $input       Input element.
		 * @param {string} option       Option name.
		 * @param {string} value        Option value.
		 * @param {Object} themeData    Theme data.
		 * @param {Object} templateData Template data.
		 */
		applyAppearanceLogicForInput( $input, option, value, themeData, templateData ) {
			switch ( option ) {
				case 'logo_url':
					app.updateLogoSrc( $input, value, app.getLogoUrl( value, themeData, templateData ) );
					break;

				case 'page_background_image':
					app.applyBackgroundImageLogic( $input );
					break;

				case 'container_border_style':
					app.applyBorderStyleLogic( $input );
					break;
			}
		},

		/**
		 * Update logo image source.
		 *
		 * @since 1.0.0
		 *
		 * @param {jQuery} $input Logo URL input.
		 * @param {string} value  Logo URL value.
		 * @param {string} src    Logo URL source.
		 */
		updateLogoSrc( $input, value, src ) {
			const $logoControl = $input.closest( '.wpforms-image-upload-control' );

			$logoControl
				.find( '.wpforms-image-upload-button' )
				.toggleClass( 'wpforms-hidden', value !== '' );

			$logoControl
				.find( '.wpforms-image-remove-button' )
				.toggleClass( 'wpforms-hidden', value === '' );

			$logoControl
				.find( '.wpforms-image-preview img' )
				.attr( 'src', src );
		},

		/**
		 * Load color pickers based on the selected theme.
		 *
		 * @param {jQuery} $block Block object.
		 */
		loadColorPickers( $block ) {
			$block = $block.hasClass( '.wpforms-pdf' ) ? $block : $block.closest( '.wpforms-pdf' );

			// Get theme colors.
			const selectedTheme = app.getSelectedThemeSlug( $block );
			const themeData = app.getThemeData( selectedTheme );
			const themeColors = themeData?.colors || {};
			const themeSwatches = Object.values( themeColors ) || [];

			WPFormsBuilder.loadColorPickers( $block, {
				swatches: themeSwatches.slice( 0, 8 ),
				skipFocused: true,
			} );
		},

		/**
		 * Get the selected theme in the given PDF block.
		 *
		 * @since 1.0.0
		 *
		 * @param {jQuery} $block Block element.
		 *
		 * @return {string|*} The selected theme slug.
		 */
		getSelectedThemeSlug( $block ) {
			return $block.find( '.wpforms-field-pdf-theme select' ).val();
		},

		/**
		 * Toggle appearance settings visibility based on template category.
		 *
		 * @since 1.0.0
		 *
		 * @param {jQuery} $categorySelect The category 'select' element.
		 */
		toggleAppearanceSettingsVisibility( $categorySelect ) {
			const selectedCategory = $categorySelect.val();
			const categoryType = selectedCategory === 'notification' ? 'notification' : 'general';
			const $blockContent = $categorySelect.closest( '.wpforms-builder-settings-block-content' );

			// Hide all appearance settings sections first.
			const $allAppearanceSettings = $blockContent.find( '.wpforms-pdf-appearance-settings' );
			$allAppearanceSettings.hide();

			// Show only the relevant section based on a category type.
			$blockContent.find( `.wpforms-pdf-appearance-settings[data-category="${ categoryType }"]` ).show();
		},

		/**
		 * Get all themes data.
		 *
		 * @since 1.0.0
		 *
		 * @return {Object} Themes data.
		 */
		getAllThemes() {
			return {
				...wpformsPDF.customThemes,
				...wpformsPDF.themes,
			};
		},

		/**
		 * Get the theme data.
		 *
		 * @since 1.0.0
		 *
		 * @param {string} slug Theme slug.
		 *
		 * @return {Object|null} Theme data.
		 */
		getThemeData( slug ) {
			const wpformsThemeData = wpformsPDF.themes[ slug ] ?? null;
			const customThemeData = wpformsPDF.customThemes[ slug ] ?? null;
			const themeData = customThemeData || wpformsThemeData;

			if ( ! themeData ) {
				return null;
			}

			themeData.isCustom = Boolean( customThemeData );

			return { ...themeData };
		},

		/**
		 * Get a theme colors array.
		 *
		 * @since 1.0.0
		 *
		 * @param {string} themeId Theme ID.
		 *
		 * @return {Array|null} Colors array or null.
		 */
		getThemeColors( themeId ) {
			return app.getThemeData( themeId )?.colors ?? null;
		},

		/**
		 * Generate HTML for color swatches.
		 *
		 * @since 1.0.0
		 *
		 * @param {Array} colors Colors array.
		 *
		 * @return {string} HTML string for swatches.
		 */
		getColorSwatchesHTML( colors ) {
			let html = '';

			if ( ! colors ) {
				return html;
			}

			// Get the first 5 colors.
			const colorsArray = Object.values( colors )?.slice( 0, 5 );

			$.each( colorsArray, function( index, color ) {
				html += `<div class="wpforms-theme-color-swatch" data-index="${ index }" style="background-color: ${ color }"></div>`;
			} );

			return html;
		},

		/**
		 * Get logo URL.
		 *
		 * @since 1.0.0
		 *
		 * @param {string}             logoUrl      Logo URL.
		 * @param {Object|string|null} themeData    Theme data.
		 * @param {Object}             templateData Template data.
		 *
		 * @return {string} Logo URL.
		 */
		getLogoUrl( logoUrl, themeData = null, templateData = null ) {
			if ( ! logoUrl.length ) {
				return '';
			}

			themeData = typeof themeData === 'string' ? app.getThemeData( themeData ) : themeData;
			templateData = typeof templateData === 'string' ? WPFormsPDFBuilder.templates.getTemplateData( templateData ) : templateData;
			logoUrl = app.getColorizedLogoImage( logoUrl, themeData, templateData );

			if ( logoUrl.startsWith( 'data:image' ) ) {
				return logoUrl;
			}

			// Get normalized URLs for comparison.
			const logoUrlNorm = app.normalizeUrl( logoUrl );
			const siteUrlNorm = app.normalizeUrl( wpformsPDF.siteUrl );

			return logoUrlNorm.includes( siteUrlNorm )
				? logoUrl
				: wpformsPDF.pluginUrl + 'assets/images/' + logoUrl;
		},

		/**
		 * Get colorized logo image.
		 *
		 * @since 1.0.0
		 *
		 * @param {string} logoUrl      Logo URL value.
		 * @param {Object} themeData    Theme data object.
		 * @param {Object} templateData Theme data object.
		 *
		 * @return {string} Logo data image if possible.
		 */
		getColorizedLogoImage( logoUrl, themeData, templateData ) {
			if ( ! themeData || ! templateData ) {
				return logoUrl;
			}

			if ( ! logoUrl.length || ! logoUrl.startsWith( 'logo/' ) || ! templateData.appearance?.logo_template ) {
				return logoUrl;
			}

			let svgContent = templateData.appearance.logo_template;

			Object.entries( themeData.colors ).forEach( ( [ key, value ] ) => {
				svgContent = svgContent.replaceAll( `{color_${ key }}`, value.toString() );
			} );

			return 'data:image/svg+xml;base64,' + btoa( svgContent );
		},

		/**
		 * Get normalized URL.
		 * For cases when the image was uploaded before enabling https on server.
		 *
		 * @since 1.0.0
		 *
		 * @param {string} url URL.
		 *
		 * @return {string} Normalized URL.
		 */
		normalizeUrl( url ) {
			// noinspection HttpUrlsUsage
			return url.replace( 'http://', 'https://' );
		},
	};

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