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;
}