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/preview-reflector.js
/* global WPFormsPDFBuilder, WPFormsUtils, wpf, wpformsPDF */

/**
 * WPForms PDF: Preview Reflector 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
	/**
	 * Implement the Reflector object.
	 *
	 * @since 1.0.0
	 *
	 * @type {Object}
	 */
	const reflector = {
		/**
		 * Current element Id.
		 *
		 * @since 1.0.0
		 *
		 * @type {Object}
		 */
		currentId: null,

		/**
		 * Data storage.
		 *
		 * @since 1.0.0
		 *
		 * @type {Object}
		 */
		data: {},

		/**
		 * Initialize module.
		 *
		 * @since 1.0.0
		 */
		init() {
			reflector.$builder = $( '#wpforms-builder' );
		},

		/**
		 * Add an element to the reflector data.
		 *
		 * @since 1.0.0
		 *
		 * @param {jQuery} $input The input element.
		 *
		 * @return {boolean} True on success.
		 */
		addElement( $input ) { // eslint-disable-line complexity
			const id = reflector.getInputId( $input );

			reflector.currentId = id;

			if ( ! id ) {
				return false;
			}

			// Skip if we have an element.
			if ( reflector.data[ id ] ) {
				return true;
			}

			// Otherwise, create the new current reflector data element.
			/* eslint-disable no-multi-spaces */
			const data = {
				id,                      // Setting input id attribute.
				$input,                  // Setting input element.
				value: null,             // Setting input value.
				pdfId: null,             // PDF block ID.
				group: null,             // PDF settings group.
				setting: '',             // Setting slug.
				pair: null,              // Pair data for the setting.
				$previewContainer: null, // Element container inside the preview iframe.
				themeColorKey: null,     // Theme color key.
				themeColorValue: null,   // Theme color value.
				themeSlug: null,         // Theme slug.
				templateValue: null,     // Template value.
				isInitialData: true,     // Whether the data is initial.
			};
			/* eslint-enable no-multi-spaces */

			// Get the input value.
			data.value = reflector.getInputVal( $input );

			// Regular expression to match the pdfId and a setting key.
			const regex = /wpforms-panel-field-pdfs-(\d+)-([a-zA-Z_\d]+)/;
			const match = id.match( regex );

			// If we have a match with the expected groups.
			if ( match && match.length === 3 ) {
				data.pdfId = Number( match[ 1 ] );
				data.settingOrginal = match[ 2 ];
				data.setting = data.settingOrginal
					.replace( 'notification_', '' )
					.replace( 'general_', '' );
			}

			data.pair = reflector.getSettingPair( data.setting );

			if ( ! data.pair ) {
				return false;
			}

			// Get the corresponding preview iframe document.
			const $previewDoc = $( WPFormsPDFBuilder.preview.getPreviewIframeDoc( data.pdfId ) );

			data.$previewContainer = $previewDoc.find( data.pair?.outline ?? data.pair?.selector );
			data.$updateContainer = data.pair?.outline ? $previewDoc.find( data.pair?.selector ) : data.$previewContainer;
			data.group = $input.closest( '.wpforms-panel-fields-group' ).data( 'group' );

			// Add the element to reflector data.
			reflector.data[ id ] = data;

			return true;
		},

		/**
		 * Update an element in the reflector data.
		 *
		 * @since 1.0.0
		 *
		 * @param {jQuery} $input  The input element.
		 * @param {string} context The element context.
		 */
		updateElement( $input, context = '' ) {
			const id = reflector.getInputId( $input );

			reflector.currentId = id;

			if ( ! id ) {
				return;
			}

			// Attempt to add the element if it doesn't exist.
			if ( ! reflector.data[ id ] && ! reflector.addElement( $input ) ) {
				return;
			}

			// Get the current reflector data element.
			let data = { ...reflector.data[ id ] };

			data.context = context;
			data.prevValue = data.value;
			data.value = reflector.getInputVal( $input );

			// Add theme color data to the element.
			data = reflector.addThemeColorDataToElement( data );
			data = reflector.normalizeElementData( data );

			// Do not proceed if the value hasn't changed.
			if ( data.value === reflector.data[ id ].value && ! data.isInitialData ) {
				return;
			}

			// After updating data, set isInitialData to false.
			data.isInitialData = false;

			// Update the reflector data storage.
			reflector.data[ id ] = { ...data };

			WPFormsUtils.triggerEvent( reflector.$builder, 'wpformsPDFReflectorElementUpdate', [ data ] );

			reflector.updateContainer( data );
		},

		/**
		 * Clear reflector data for the given PDF.
		 *
		 * @since 1.0.0
		 *
		 * @param {string} pdfId PDF block ID.
		 */
		clearData( pdfId ) {
			Object.entries( reflector.data ).forEach( ( [ key, value ] ) => {
				if ( value?.pdfId === Number( pdfId ) ) {
					delete reflector.data[ key ];
				}
			} );
		},

		/**
		 * Get input ID.
		 *
		 * @since 1.0.0
		 *
		 * @param {jQuery} $input Input element.
		 *
		 * @return {string|null} Input ID.
		 */
		getInputId( $input ) {
			const id = $input.attr( 'id' );

			if ( id ) {
				return id;
			}

			if ( $input.hasClass( 'choicesjs' ) ) {
				return $input.find( 'select.choices__input' ).attr( 'id' );
			}

			if ( $input.hasClass( 'wpforms-smart-tags-widget' ) ) {
				return $input.closest( '.wpforms-panel-field' )
					.find( '.wpforms-smart-tags-widget-original' )
					.attr( 'id' );
			}

			return null;
		},

		/**
		 * Get the value from the input element.
		 *
		 * @since 1.0.0
		 *
		 * @param {jQuery} $input Input element.
		 *
		 * @return {any} The input value.
		 */
		getInputVal( $input ) {
			const inputType = $input.attr( 'type' );

			if ( inputType === 'checkbox' ) {
				return $input.is( ':checked' );
			}

			return $input.val();
		},

		/**
		 * Get the value of a setting.
		 *
		 * @since 1.0.0
		 *
		 * @param {string} pdfId   The ID of the PDF settings section.
		 * @param {string} setting The setting slug.
		 *
		 * @return {string|null} The setting value.
		 */
		getSettingValue( pdfId, setting ) {
			if ( ! pdfId || ! setting ) {
				return null;
			}

			const $regularField = $( `#wpforms-panel-field-pdfs-${ pdfId }-${ setting }` );

			if ( $regularField.length ) {
				return $regularField.val();
			}

			const templateCategory = $( `wpforms-panel-field-pdfs-${ pdfId }-template_category` ).val();
			const prefix = templateCategory === 'notification' ? 'notification' : 'general';
			const $input = $( `#wpforms-panel-field-pdfs-${ pdfId }-${ prefix }_${ setting }` );

			return $input.val();
		},

		/**
		 * Add theme color data to the element.
		 *
		 * @since 1.0.0
		 *
		 * @param {Object} element Element data object.
		 *
		 * @return {Object} Element data object.
		 */
		addThemeColorDataToElement( element ) { // eslint-disable-line complexity
			const templateSlug = $( `#wpforms-panel-field-pdfs-${ element.pdfId }-template_style` ).val();
			const templateData = WPFormsPDFBuilder.templates.getTemplateData( templateSlug );

			if ( ! templateData ) {
				return element;
			}

			const templateValue = templateData.appearance?.[ element.setting ] || templateData.text?.[ element.setting ];
			const templateColorKey = /^\{color_\w+}$/.test( templateValue )
				? templateValue.replace( '{color_', '' ).replace( '}', '' )
				: null;

			const themeEditorColorKey = /^theme_color_\w+$/.test( element.setting )
				? element.setting.replace( 'theme_color_', '' )
				: null;

			const themeColorKey = templateColorKey || themeEditorColorKey;

			const themeSlug = $( `#wpforms-panel-field-pdfs-${ element.pdfId }-theme` ).val();
			const themeData = WPFormsPDFBuilder.appearance.getThemeData( themeSlug );
			const isThemeColorChange = Boolean( themeEditorColorKey );
			const themeColorValue = isThemeColorChange ? element.value : ( themeData?.colors[ themeColorKey ] ?? null );

			return {
				...element,
				...{ themeSlug, themeColorKey, themeColorValue, templateValue, templateSlug, isThemeColorChange },
			};
		},

		/**
		 * Normalize element data.
		 *
		 * @since 1.0.0
		 *
		 * @param {Object} element Element data object.
		 *
		 * @return {Object} Element data object.
		 */
		normalizeElementData( element ) {
			// Empty and invalid colors are transparent.
			if ( element.setting?.includes( '_color' ) ) {
				element.value = WPFormsUtils.cssColorsUtils.isValidColor( element.value ) ? element.value : '#ffffff00';
			}

			// Templates contain images that should be turned into full URLs.
			if ( element.setting === 'logo_url' ) {
				element.value = WPFormsPDFBuilder.appearance.getLogoUrl( element.value, element.themeSlug, element.templateSlug );
			}

			// Apply actual font name to the element.
			if ( element.setting === 'font' ) {
				element.previewValue = wpformsPDF.fonts[ element.value ];
			}

			return element;
		},

		/**
		 * Get the element data.
		 *
		 * @since 1.0.0
		 *
		 * @param {string} id The element ID.
		 *
		 * @return {Object} Element data object.
		 */
		getElement( id = '' ) {
			return reflector.data[ id ] ?? reflector.data[ reflector.currentId ];
		},

		/**
		 * Initialize pair definitions.
		 *
		 * @since 1.0.0
		 */
		initPairDefinitions() { // eslint-disable-line max-lines-per-function
			if ( reflector.pairDefinitions ) {
				return;
			}

			// Define setting-preview pairs.
			/* eslint-disable camelcase */
			reflector.pairDefinitions = {
				theme_color: {
					selector: '.page-background',
				},
				logo_url: {
					selector: 'img.logo',
					action: 'setAttribute',
					attr: 'src',
				},
				logo_size: {
					selector: '.logo-container',
					outline: 'img.logo',
					action: 'setAttribute',
					attr: 'data-size',
				},
				logo_position: {
					selector: '.header, .logo-container',
					outline: '.logo-container',
					action: 'setAttribute',
					attr: 'data-align',
				},
				font: {
					selector: '.container-content *, .container-background *',
					outline: '.container-background-fill',
					property: 'font-family',
				},
				font_size: {
					selector: '.container-content *, .container-background *',
					outline: '.container-background-fill',
					property: 'font-size',
				},
				text_color: {
					selector: '.container-content .content *',
					outline: '.container-background-fill',
					property: 'color',
				},
				content_color: {
					selector: '.container-content .content, .container-content .content *',
					outline: '.content',
					property: 'color',
				},
				link_color: {
					selector: '.container-content .content a',
					property: 'color',
				},
				page_background_color: {
					selector: '.page-background',
					property: 'background',
					conditional: {
						condition: '!=',
						setting: 'page_background_image',
						value: 'none',
						action: 'refreshPreview',
					},
				},
				page_background_color_end: {
					selector: '.page-background',
					conditional: {
						condition: '!=',
						setting: 'page_background_image',
						value: 'none',
						action: 'refreshPreview',
					},
				},
				page_background_image: {
					selector: '.page-background',
					action: 'refreshPreviewBackground',
				},
				container_background_color: {
					selector: '.container-background-fill',
					outline: '.container-background-fill',
					property: 'background-color',
				},
				container_shadow: {
					selector: '.container-shadow',
					outline: '.container-background-fill',
					action: 'setAttribute',
					attr: 'data-shadow',
				},
				container_border_style: {
					selector: '.container-background-fill',
					property: 'border-style',
				},
				container_border_size: {
					selector: '.container-background-fill',
					property: 'border-width',
				},
				container_border_color: {
					selector: '.container-background-fill',
					property: 'border-color',
				},
				container_border_radius: {
					selector: '.container-background-fill',
					property: 'border-radius',
				},
				signature_size: {
					selector: '.signature_image',
					outline: '.signature',
					action: 'setAttribute',
					attr: 'data-size',
				},
				signature_position: {
					selector: '.signature_image',
					outline: '.signature',
					action: 'setAttribute',
					attr: 'data-align',
				},
				signature_url: {
					selector: '.signature_image',
					action: 'setAttribute',
					attr: 'src',
				},
				signature_type: {
					selector: '.signature',
					action: 'setAttribute',
					attr: 'data-type',
				},
				signature_show: {
					selector: '.signature',
					action: 'toggleClassInverse',
					class: 'hidden',
				},
				badge_show: {
					selector: '.badge',
					action: 'toggleClassInverse',
					class: 'hidden',
				},
				orientation: {
					selector: '.page-background',
					action: 'changePageOrientation',
				},
			};
			/* eslint-enable camelcase */
		},

		/**
		 * Get the preview pair data for a given setting.
		 *
		 * @since 1.0.0
		 *
		 * @param {string} setting Setting slug.
		 *
		 * @return {Object} Pair data object.
		 */
		getSettingPair( setting ) {
			if ( ! setting ) {
				return null;
			}

			const data = reflector.pairDefinitions[ setting ];

			if ( data ) {
				return data;
			}

			// Theme color settings.
			if ( setting.includes( 'theme_color_' ) ) {
				return reflector.pairDefinitions.theme_color;
			}

			// Text color settings.
			if ( setting.includes( '_color' ) ) {
				const selector = setting.replace( '_color', '' );

				return {
					selector: `.${ selector }, .${ selector } *`,
					outline: `.${ selector }`,
					property: 'color',
				};
			}

			// Replace text is a default action for all other settings.
			return {
				selector: '.' + setting,
				action: 'replaceText',
			};
		},

		/**
		 * Highlight container.
		 *
		 * @since 1.0.0
		 *
		 * @param {jQuery} $container Container jQuery object.
		 */
		highLightContainer( $container ) {
			reflector.removeHighLighting();
			$container.addClass( 'preview-highlight' );
		},

		/**
		 * Remove highlightings.
		 *
		 * @since 1.0.0
		 */
		removeHighLighting() {
			const doc = WPFormsPDFBuilder.preview.getPreviewIframeDoc();

			$( doc ).find( '.preview-highlight' ).removeClass( 'preview-highlight' );
		},

		/**
		 * Update container.
		 *
		 * @since 1.0.0
		 *
		 * @param {Object} data Reflector element data.
		 */
		updateContainer( data ) {
			if ( ! data || ! data.$updateContainer?.length ) {
				return;
			}

			const action = data.pair.action ?? 'injectStyle';
			const actionCallback = reflectorActions[ action ]; // eslint-disable-line no-use-before-define

			if ( typeof actionCallback === 'function' ) {
				actionCallback( data );
			}

			// Run conditional action.
			reflector.conditionalAction( data );
		},

		/**
		 * Run conditional action.
		 *
		 * @since 1.0.0
		 *
		 * @param {Object} data Pair data.
		 */
		conditionalAction( data ) { // eslint-disable-line complexity
			if ( ! data.pair?.conditional ) {
				return;
			}

			const cond = data.pair.conditional;

			if ( ! cond.setting || ! cond.action || ! cond.condition ) {
				return;
			}

			const settingValue = reflector.getSettingValue( data.pdfId, cond.setting );

			// Define process condition logic.
			const process = {
				e: () => {
					return ! Boolean( settingValue );
				},
				'!e': () => {
					return Boolean( settingValue );
				},
				'==': () => {
					return settingValue === cond.value;
				},
				'!=': () => {
					return settingValue !== cond.value;
				},
				'>': () => {
					return settingValue > cond.value;
				},
				'>=': () => {
					return settingValue >= cond.value;
				},
				'<': () => {
					return settingValue < cond.value;
				},
				'<=': () => {
					return settingValue <= cond.value;
				},
			};

			if ( ! process[ cond.condition ] || ! process[ cond.condition ]() ) {
				return;
			}

			const actionCallback = reflectorActions[ cond.action ]; // eslint-disable-line no-use-before-define

			if ( typeof actionCallback !== 'function' ) {
				return;
			}

			actionCallback( data );
		},
	};

	/**
	 * The Reflector actions object.
	 *
	 * @since 1.0.0
	 *
	 * @type {Object}
	 */
	const reflectorActions = {
		/**
		 * Injects a new style or updates an existing style.
		 *
		 * @since 1.0.0
		 *
		 * @param {Object} data Pair data object.
		 */
		injectStyle( data ) { // eslint-disable-line complexity
			if ( ! data || ! data.$updateContainer?.length ) {
				return;
			}

			const $body = data.$updateContainer.closest( 'body' );
			const previewValue = data.previewValue ?? data.value;

			// Set --wpforms-theme-color-{ data.themeColorKey } CSS variable.
			if ( data.themeColorKey ) {
				$body.css( `--wpforms-theme-color-${ data.themeColorKey }`, data.themeColorValue );
			}

			// Set --wpforms-appearance-{ data.setting } CSS variable.
			if ( data.group === 'settings_pdf_appearance' ) {
				$body.css( `--wpforms-appearance-${ data.setting }`, previewValue );
			}

			// Set --wpforms-text-color-{ data.setting } CSS variable.
			if ( data.group === 'settings_pdf_template' && data.setting.includes( '_color' ) ) {
				$body.css( `--wpforms-text-color-${ data.setting }`, data.value );
			}
		},

		/**
		 * Replace text.
		 *
		 * @since 1.0.0
		 *
		 * @param {Object} data Pair data object.
		 */
		replaceText( data ) {
			data.$updateContainer.html( wpf.sanitizeHTML( data.value ) );
		},

		/**
		 * Toggle CSS class (inverse logic).
		 *
		 * @since 1.0.0
		 *
		 * @param {Object} data Pair data object.
		 */
		toggleClassInverse( data ) {
			data.$updateContainer.toggleClass( data.pair.class, ! data.value );
		},

		/**
		 * Set attribute.
		 *
		 * @since 1.0.0
		 *
		 * @param {Object} data Pair data object.
		 */
		setAttribute( data ) {
			data.$updateContainer.attr( data.pair.attr, data.value );
		},

		/**
		 * Refresh the preview.
		 *
		 * @since 1.0.0
		 *
		 * @param {Object} data Pair data object.
		 */
		refreshPreview( data ) {
			if ( ! data.pdfId ) {
				return;
			}

			WPFormsPDFBuilder.preview.fetchPreview( data.pdfId );
		},

		/**
		 * Refresh the preview on change of the background image.
		 *
		 * @since 1.0.0
		 *
		 * @param {Object} data Pair data object.
		 */
		refreshPreviewBackground( data ) {
			if ( ! data.pdfId ) {
				return;
			}

			if ( data.value === 'none' && data.prevValue === 'none' ) {
				return;
			}

			WPFormsPDFBuilder.preview.fetchPreview( data.pdfId );
		},

		/**
		 * Change page orientation.
		 *
		 * @since 1.0.0
		 *
		 * @param {Object} data Pair data object.
		 */
		changePageOrientation( data ) {
			if ( ! data.pdfId ) {
				return;
			}

			reflector.$preview = reflector.$preview ?? $( '#wpforms-pdf-preview' );
			reflector.$preview.attr( 'data-orientation', data.value );

			WPFormsPDFBuilder.preview.fetchPreview( data.pdfId );
		},
	};

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