Snapshot: MLS sync fixes, image refresh, plugin/theme updates
MLS plugin fixes from this session: - Fix silent insert failures: location column NOT NULL was rejecting wpdb->insert calls, causing ~18k new properties since Dec 2025 to be lost. Inserts now build raw SQL with ST_PointFromText so the spatial column is populated atomically. - Auto-refresh expired media URLs in MLS_Media_Handler::fetch_and_cache(), guarded by a property-level GET_LOCK so concurrent fetches share one API refresh. - Normalize WP_Error to null in mls_get_property_image() so callers can rely on the documented string|null contract. - Support comma-separated property_type filters in MLS_Query and MLS_Cluster so the homepage "View All Commercial" link (?property_type=Commercial+Sale,Land,Farm) actually filters correctly. - Incremental sync now looks back 10 minutes past the latest modification timestamp as a safety margin against missed records. - Smart sync exits silently (info-level, not warning) when a full sync is in progress. Operational: - New cron: weekly full sync Sundays at 3 AM (/usr/local/bin/mls-full-sync). - New cron: hourly 2GB cap on mls-thumbnails/ and cache/transformed-images/ (/usr/local/bin/mls-image-cache-cap). - Logrotate config for wp-content/debug.log (2-day retention, daily rotation, delaycompress). Repo policy: - CLAUDE.md updated with explicit "commit everything except build artifacts" policy. - .gitignore: untrack runtime image caches and debug.log rotations. Other modifications in this snapshot are pre-existing in-flight theme/plugin/db_content_updates work. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Executable
+109
@@ -0,0 +1,109 @@
|
||||
/* global wpforms_builder_themes_no_access */
|
||||
|
||||
/**
|
||||
* @param wpforms_builder_themes_no_access.strings.permission_modal.confirm
|
||||
* @param wpforms_builder_themes_no_access.strings.permission_modal.content
|
||||
* @param wpforms_builder_themes_no_access.strings.permission_modal.title
|
||||
*/
|
||||
|
||||
// noinspection ES6ConvertVarToLetConst
|
||||
/**
|
||||
* WPForms Form builder themes - Non-admin version.
|
||||
*
|
||||
* @since 1.9.8
|
||||
*/
|
||||
var WPForms = window.WPForms || {}; // eslint-disable-line no-var
|
||||
|
||||
WPForms.Admin = WPForms.Admin || {};
|
||||
WPForms.Admin.Builder = WPForms.Admin.Builder || {};
|
||||
|
||||
WPForms.Admin.Builder.ThemesNoAccess = WPForms.Admin.Builder.ThemesNoAccess || ( function( document, window, $ ) {
|
||||
/**
|
||||
* Elements holder.
|
||||
*
|
||||
* @since 1.9.8
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const el = {};
|
||||
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.9.8
|
||||
*/
|
||||
const app = {
|
||||
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.9.8
|
||||
*/
|
||||
init() {
|
||||
$( app.ready );
|
||||
},
|
||||
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.9.8
|
||||
*/
|
||||
ready() {
|
||||
app.setup();
|
||||
app.events();
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup. Prepare some variables.
|
||||
*
|
||||
* @since 1.9.8
|
||||
*/
|
||||
setup() {
|
||||
// Cache DOM elements.
|
||||
el.$builder = $( '#wpforms-builder' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup events.
|
||||
*
|
||||
* @since 1.9.8
|
||||
*/
|
||||
events() {
|
||||
el.$builder.on( 'wpformsPanelSectionSwitch', app.handlePanelSectionSwitch );
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle panel section switch and show permission modal.
|
||||
*
|
||||
* @since 1.9.8
|
||||
*
|
||||
* @param {Object} _event The event object.
|
||||
* @param {string} section The section that was switched to.
|
||||
*/
|
||||
handlePanelSectionSwitch( _event, section ) {
|
||||
if ( section === 'themes' ) {
|
||||
$.alert( {
|
||||
title: wpforms_builder_themes_no_access.strings.permission_modal.title,
|
||||
content: wpforms_builder_themes_no_access.strings.permission_modal.content,
|
||||
icon: 'fa fa-exclamation-triangle',
|
||||
type: 'red',
|
||||
theme: 'modern',
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: wpforms_builder_themes_no_access.strings.permission_modal.confirm,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
},
|
||||
},
|
||||
} );
|
||||
_event.preventDefault();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// Return the public-facing methods.
|
||||
return app;
|
||||
}( document, window, jQuery ) );
|
||||
|
||||
// Initialize.
|
||||
WPForms.Admin.Builder.ThemesNoAccess.init();
|
||||
Vendored
Executable
+1
@@ -0,0 +1 @@
|
||||
var WPForms=window.WPForms||{};WPForms.Admin=WPForms.Admin||{},WPForms.Admin.Builder=WPForms.Admin.Builder||{},WPForms.Admin.Builder.ThemesNoAccess=WPForms.Admin.Builder.ThemesNoAccess||(n=>{let e={},s={init(){n(s.ready)},ready(){s.setup(),s.events()},setup(){e.$builder=n("#wpforms-builder")},events(){e.$builder.on("wpformsPanelSectionSwitch",s.handlePanelSectionSwitch)},handlePanelSectionSwitch(e,s){"themes"===s&&(n.alert({title:wpforms_builder_themes_no_access.strings.permission_modal.title,content:wpforms_builder_themes_no_access.strings.permission_modal.content,icon:"fa fa-exclamation-triangle",type:"red",theme:"modern",buttons:{confirm:{text:wpforms_builder_themes_no_access.strings.permission_modal.confirm,btnClass:"btn-confirm",keys:["enter"]}}}),e.preventDefault())}};return s})((document,window,jQuery)),WPForms.Admin.Builder.ThemesNoAccess.init();
|
||||
+264
@@ -0,0 +1,264 @@
|
||||
/* global wpforms_builder_themes, wpf, WPFormsUtils */
|
||||
|
||||
// noinspection ES6ConvertVarToLetConst
|
||||
/**
|
||||
* WPForms Form builder themes.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
var WPForms = window.WPForms || {}; // eslint-disable-line no-var
|
||||
|
||||
WPForms.Admin = WPForms.Admin || {};
|
||||
WPForms.Admin.Builder = WPForms.Admin.Builder || {};
|
||||
|
||||
WPForms.Admin.Builder.Themes = WPForms.Admin.Builder.Themes || ( function( document, window, $ ) {
|
||||
/**
|
||||
* Elements holder.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const el = {};
|
||||
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
const app = {
|
||||
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
init() {
|
||||
$( app.ready );
|
||||
},
|
||||
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
ready() {
|
||||
app.setup();
|
||||
app.loadModules();
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup. Prepare some variables.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
setup() {
|
||||
// Cache DOM elements.
|
||||
el.$builder = $( '#wpforms-builder' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Centralized reactive store for theme style settings in the Form Builder.
|
||||
*
|
||||
* Provides a simple pub/sub mechanism to track and respond to setting changes.
|
||||
* Automatically initializes its state from all inputs/selects matching `name="settings[themes][...]"`.
|
||||
*
|
||||
* Usage examples:
|
||||
*
|
||||
* // Subscribe to a specific setting change.
|
||||
* app.store.subscribe('buttonTextColor', (value) => {
|
||||
* console.log('Button text color changed to:', value);
|
||||
* } );
|
||||
*
|
||||
* // Subscribe to all setting changes.
|
||||
* app.store.subscribeAll(( value, key) => {
|
||||
* console.log( Setting ${ key } changed to:`, value);
|
||||
* });
|
||||
*
|
||||
* // Get current value.
|
||||
* const padding = app.store.get('containerPadding');
|
||||
*
|
||||
* // Manually update a setting (will trigger listeners).
|
||||
* // Use the 3rd argument as 'true' to set in a 'silent' mode when state and input will update,
|
||||
* // but no listeners will be run.
|
||||
* app.store.set('fieldSize', 'medium');
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @type {Object}
|
||||
* @property {Function} get Get current value of a setting by key.
|
||||
* @property {Function} set Set value of a setting by key and notify listeners.
|
||||
* @property {Function} subscribe Subscribe to a specific setting change: (key, callback) => void
|
||||
* @property {Function} subscribeAll Subscribe to all setting changes: (callback) => void
|
||||
* @property {Function} initFromDOM Initialize the store from DOM inputs/selects.
|
||||
* @property {Object} state Raw internal state object (mostly for debugging).
|
||||
*/
|
||||
store: ( () => {
|
||||
const state = {};
|
||||
const keyListeners = new Map();
|
||||
const inputElements = new Map();
|
||||
const globalListeners = [];
|
||||
const debouncedSetters = {};
|
||||
const DEBOUNCE_DELAY = 50;
|
||||
|
||||
// Settings getter.
|
||||
const get = ( key ) => state[ key ];
|
||||
|
||||
// Settings setter.
|
||||
const set = ( key, value, silent = false ) => {
|
||||
if ( state[ key ] === value ) {
|
||||
return;
|
||||
}
|
||||
|
||||
state[ key ] = value;
|
||||
|
||||
const $input = inputElements.get( key );
|
||||
|
||||
if ( $input && $input.val() !== value ) {
|
||||
$input.val( value );
|
||||
if ( ! silent ) {
|
||||
$input.trigger( 'input' );
|
||||
}
|
||||
}
|
||||
|
||||
if ( silent ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( keyListeners.has( key ) ) {
|
||||
$.each( keyListeners.get( key ), ( _, cb ) => cb( value, key ) );
|
||||
}
|
||||
$.each( globalListeners, ( _, cb ) => cb( value, key ) );
|
||||
};
|
||||
|
||||
// Get bounced Setter.
|
||||
const getDebouncedSetter = ( key ) => {
|
||||
if ( ! debouncedSetters[ key ] ) {
|
||||
debouncedSetters[ key ] = _.debounce( ( value ) => set( key, value ), DEBOUNCE_DELAY );
|
||||
}
|
||||
return debouncedSetters[ key ];
|
||||
};
|
||||
|
||||
// Allow subscribing to specific setting change.
|
||||
const subscribe = ( key, callback ) => {
|
||||
if ( ! keyListeners.has( key ) ) {
|
||||
keyListeners.set( key, [] );
|
||||
}
|
||||
keyListeners.get( key ).push( callback );
|
||||
};
|
||||
|
||||
// Allow listening all settings change.
|
||||
const subscribeAll = ( callback ) => {
|
||||
globalListeners.push( callback );
|
||||
};
|
||||
|
||||
// Initialize from DOM (should be called once during app init).
|
||||
const initFromDOM = () => {
|
||||
$( '[name^="settings[themes]"]' ).each( function() {
|
||||
const $input = $( this );
|
||||
const nameMatch = $input.attr( 'name' ).match( /\[themes]\[(.*?)]/ );
|
||||
if ( ! nameMatch ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const key = nameMatch[ 1 ];
|
||||
state[ key ] = $input.val();
|
||||
inputElements.set( key, $input );
|
||||
|
||||
const tag = $input.prop( 'tagName' ).toLowerCase();
|
||||
const type = ( $input.attr( 'type' ) || '' ).toLowerCase();
|
||||
|
||||
const isDebouncedInput = (
|
||||
( tag === 'input' && ( type === 'text' || type === 'number' || type === 'hidden' ) ) ||
|
||||
tag === 'textarea'
|
||||
);
|
||||
|
||||
$input.on( isDebouncedInput ? 'input' : 'change', function() {
|
||||
const value = $( this ).val();
|
||||
if ( isDebouncedInput ) {
|
||||
getDebouncedSetter( key )( value );
|
||||
} else {
|
||||
set( key, value );
|
||||
}
|
||||
} );
|
||||
} );
|
||||
};
|
||||
|
||||
return {
|
||||
get,
|
||||
set,
|
||||
subscribe,
|
||||
subscribeAll,
|
||||
initFromDOM,
|
||||
inputElements,
|
||||
state,
|
||||
};
|
||||
}
|
||||
)(),
|
||||
|
||||
/**
|
||||
* Get setting.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {null|string} key Settings key.
|
||||
*
|
||||
* @return {Object} Setting value.
|
||||
*/
|
||||
getSettings( key = null ) {
|
||||
if ( key ) {
|
||||
return app.store.get( key );
|
||||
}
|
||||
return app.store.state;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get controls.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {null|string} key Settings key.
|
||||
*
|
||||
* @return {Object} Control
|
||||
*/
|
||||
getControls( key = null ) {
|
||||
if ( key ) {
|
||||
return app.store.inputElements.get( key );
|
||||
}
|
||||
return app.store.inputElements;
|
||||
},
|
||||
|
||||
/**
|
||||
* Load modules.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
loadModules() {
|
||||
const modules = wpforms_builder_themes.modules || [];
|
||||
|
||||
// Import all modules dynamically.
|
||||
Promise.all( modules.map( ( module ) => import( module.path ) ) )
|
||||
.then( ( importedModules ) => {
|
||||
importedModules.forEach( ( module, index ) => {
|
||||
const moduleName = modules[ index ].name;
|
||||
app[ moduleName ] = module.default( document, window, $ );
|
||||
|
||||
// Initialize module on `wpformsBuilderThemesLoaded` event.
|
||||
el.$builder.on( `wpformsBuilderThemesLoaded`, app[ moduleName ].init );
|
||||
} );
|
||||
|
||||
// Trigger `wpformsBuilderThemesLoaded` event.
|
||||
WPFormsUtils.triggerEvent( el.$builder, 'wpformsBuilderThemesLoaded', [ importedModules ] );
|
||||
} )
|
||||
.catch( ( error ) => {
|
||||
wpf.debug( 'Error importing modules:', error );
|
||||
} );
|
||||
},
|
||||
};
|
||||
|
||||
// Return the public-facing methods.
|
||||
return app;
|
||||
}( document, window, jQuery ) );
|
||||
|
||||
// Initialize.
|
||||
WPForms.Admin.Builder.Themes.init();
|
||||
Vendored
Executable
+1
@@ -0,0 +1 @@
|
||||
var WPForms=window.WPForms||{};WPForms.Admin=WPForms.Admin||{},WPForms.Admin.Builder=WPForms.Admin.Builder||{},WPForms.Admin.Builder.Themes=WPForms.Admin.Builder.Themes||((s,i,d)=>{let n={},o={init(){d(o.ready)},ready(){o.setup(),o.loadModules()},setup(){n.$builder=d("#wpforms-builder")},store:(()=>{let n={},i=new Map,o=new Map,m=[],a={},l=50;let u=(r,s,e=!1)=>{var t;n[r]===s||(n[r]=s,(t=o.get(r))&&t.val()!==s&&(t.val(s),e||t.trigger("input")),e)||(i.has(r)&&d.each(i.get(r),(e,t)=>t(s,r)),d.each(m,(e,t)=>t(s,r)))};return{get:e=>n[e],set:u,subscribe:(e,t)=>{i.has(e)||i.set(e,[]),i.get(e).push(t)},subscribeAll:e=>{m.push(e)},initFromDOM:()=>{d('[name^="settings[themes]"]').each(function(){var e=d(this),t=e.attr("name").match(/\[themes]\[(.*?)]/);if(t){let r=t[1];n[r]=e.val(),o.set(r,e);var t=e.prop("tagName").toLowerCase(),i=(e.attr("type")||"").toLowerCase();let s="input"===t&&("text"===i||"number"===i||"hidden"===i)||"textarea"===t;e.on(s?"input":"change",function(){var t,e=d(this).val();s?(t=r,a[t]||(a[t]=_.debounce(e=>u(t,e),l)),a[t])(e):u(r,e)})}})},inputElements:o,state:n}})(),getSettings(e=null){return e?o.store.get(e):o.store.state},getControls(e=null){return e?o.store.inputElements.get(e):o.store.inputElements},loadModules(){let r=wpforms_builder_themes.modules||[];Promise.all(r.map(e=>import(e.path))).then(e=>{e.forEach((e,t)=>{t=r[t].name;o[t]=e.default(s,i,d),n.$builder.on("wpformsBuilderThemesLoaded",o[t].init)}),WPFormsUtils.triggerEvent(n.$builder,"wpformsBuilderThemesLoaded",[e])}).catch(e=>{wpf.debug("Error importing modules:",e)})}};return o})(document,window,jQuery),WPForms.Admin.Builder.Themes.init();
|
||||
Executable
+221
@@ -0,0 +1,221 @@
|
||||
/* global wpforms_builder_themes */
|
||||
|
||||
/**
|
||||
* WPForms Form Builder Themes: Advanced settings module.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @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 max-lines-per-function
|
||||
const WPForms = window.WPForms || {};
|
||||
const WPFormsBuilderThemes = WPForms.Admin.Builder.Themes || {};
|
||||
|
||||
/**
|
||||
* Elements holder.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const el = {};
|
||||
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
const app = {
|
||||
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
init() {
|
||||
app.setup();
|
||||
app.events();
|
||||
|
||||
// Subscribe to all settings change.
|
||||
WPFormsBuilderThemes.store.subscribeAll( ( value, key ) => {
|
||||
app.updateCopyPasteContent( value, key );
|
||||
} );
|
||||
|
||||
app.disableSpellCheck();
|
||||
app.updateCopyPasteContent();
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
setup() {
|
||||
el.$builder = $( '#wpforms-builder' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
events() {
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the list of the settings key allowed to show in the Copy/paste field.
|
||||
*
|
||||
* @since 1.9.7
|
||||
* @return {Array} List of allowed settings.
|
||||
*/
|
||||
getAllowedKeys() {
|
||||
const allowedKeys = [ 'themeName', 'isCustomTheme', 'wpformsTheme', 'customCss' ];
|
||||
const styleSettings = WPFormsBuilderThemes.common.getStyleAttributesKeys();
|
||||
|
||||
return allowedKeys.concat( styleSettings );
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the content of the "Copy/Paste" field.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} value Setting value
|
||||
* @param {string} key Setting key.
|
||||
*/
|
||||
updateCopyPasteContent( value = '', key = '' ) {
|
||||
if ( key === 'copyPasteJsonValue' ) {
|
||||
app.pasteSettings( value );
|
||||
return;
|
||||
}
|
||||
|
||||
const content = {};
|
||||
const allowedKeys = app.getAllowedKeys();
|
||||
const settings = WPFormsBuilderThemes.getSettings();
|
||||
|
||||
allowedKeys.forEach( ( settingKey ) => {
|
||||
content[ settingKey ] = settings[ settingKey ];
|
||||
} );
|
||||
|
||||
// Update field content in a 'silent' mode.
|
||||
WPFormsBuilderThemes.store.set( 'copyPasteJsonValue', JSON.stringify( content ), true );
|
||||
},
|
||||
|
||||
/**
|
||||
* Paste settings handler.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} value New attribute value.
|
||||
*/
|
||||
pasteSettings( value ) {
|
||||
value = value.trim();
|
||||
|
||||
const pasteAttributes = app.parseValidateJson( value );
|
||||
|
||||
// Show the error modal if JSON is broken.
|
||||
if ( ! pasteAttributes ) {
|
||||
if ( value ) {
|
||||
app.showJsonErrorModal();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const themeSlug = pasteAttributes?.wpformsTheme ?? pasteAttributes?.theme;
|
||||
const currentThemeSlug = WPFormsBuilderThemes.store.get( 'wpformsTheme' );
|
||||
const theme = WPFormsBuilderThemes.themes.getTheme( themeSlug );
|
||||
|
||||
// If the theme already exists - set it.
|
||||
if ( theme && themeSlug !== currentThemeSlug ) {
|
||||
WPFormsBuilderThemes.themes.setFormTheme( themeSlug );
|
||||
WPFormsBuilderThemes.themes.updateThemesList();
|
||||
return;
|
||||
}
|
||||
|
||||
// For not existed theme - parse and set settings.
|
||||
const allowedKeys = app.getAllowedKeys();
|
||||
|
||||
allowedKeys.forEach( ( settingKey ) => {
|
||||
if ( pasteAttributes[ settingKey ] !== undefined ) {
|
||||
let settingValue = pasteAttributes[ settingKey ];
|
||||
|
||||
settingValue = typeof settingValue === 'string'
|
||||
? settingValue.replace( /px$/, '' )
|
||||
: settingValue;
|
||||
|
||||
WPFormsBuilderThemes.store.set( settingKey, settingValue );
|
||||
}
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse and validate JSON string.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} value JSON string.
|
||||
*
|
||||
* @return {boolean|object} Parsed JSON object OR false on error.
|
||||
*/
|
||||
parseValidateJson( value ) {
|
||||
if ( typeof value !== 'string' ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let atts;
|
||||
|
||||
try {
|
||||
atts = JSON.parse( value.trim() );
|
||||
} catch ( error ) {
|
||||
atts = false;
|
||||
}
|
||||
|
||||
return atts;
|
||||
},
|
||||
|
||||
/**
|
||||
* Show the error when pasted JSON is broken.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
showJsonErrorModal() {
|
||||
$.alert( {
|
||||
title: wpforms_builder_themes.strings.uhoh,
|
||||
content: wpforms_builder_themes.strings.copy_paste_error,
|
||||
icon: 'fa fa-exclamation-circle',
|
||||
type: 'red',
|
||||
buttons: {
|
||||
cancel: {
|
||||
text: wpforms_builder_themes.strings.close,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
},
|
||||
},
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Disable spellcheck for the textarea settings fields.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
disableSpellCheck() {
|
||||
const customCssControl = WPFormsBuilderThemes.getControls( 'customCss' );
|
||||
const copyPasteControl = WPFormsBuilderThemes.getControls( 'copyPasteJsonValue' );
|
||||
|
||||
if ( ! customCssControl || ! copyPasteControl ) {
|
||||
return;
|
||||
}
|
||||
|
||||
copyPasteControl.attr( 'spellcheck', 'false' );
|
||||
customCssControl.attr( 'spellcheck', 'false' );
|
||||
},
|
||||
};
|
||||
|
||||
return app;
|
||||
}
|
||||
Vendored
Executable
+1
@@ -0,0 +1 @@
|
||||
export default function(e,t,s){let o=(t.WPForms||{}).Admin.Builder.Themes||{},r={},l={init(){l.setup(),l.events(),o.store.subscribeAll((e,t)=>{l.updateCopyPasteContent(e,t)}),l.disableSpellCheck(),l.updateCopyPasteContent()},setup(){r.$builder=s("#wpforms-builder")},events(){},getAllowedKeys(){var e=o.common.getStyleAttributesKeys();return["themeName","isCustomTheme","wpformsTheme","customCss"].concat(e)},updateCopyPasteContent(e="",r=""){if("copyPasteJsonValue"===r)l.pasteSettings(e);else{let t={};r=l.getAllowedKeys();let s=o.getSettings();r.forEach(e=>{t[e]=s[e]}),o.store.set("copyPasteJsonValue",JSON.stringify(t),!0)}},pasteSettings(e){e=e.trim();let s=l.parseValidateJson(e);var t,r;s?(t=s?.wpformsTheme??s?.theme,r=o.store.get("wpformsTheme"),o.themes.getTheme(t)&&t!==r?(o.themes.setFormTheme(t),o.themes.updateThemesList()):l.getAllowedKeys().forEach(t=>{if(void 0!==s[t]){let e=s[t];e="string"==typeof e?e.replace(/px$/,""):e,o.store.set(t,e)}})):e&&l.showJsonErrorModal()},parseValidateJson(e){if("string"!=typeof e)return!1;let t;try{t=JSON.parse(e.trim())}catch(e){t=!1}return t},showJsonErrorModal(){s.alert({title:wpforms_builder_themes.strings.uhoh,content:wpforms_builder_themes.strings.copy_paste_error,icon:"fa fa-exclamation-circle",type:"red",buttons:{cancel:{text:wpforms_builder_themes.strings.close,btnClass:"btn-confirm",keys:["enter"]}}})},disableSpellCheck(){var e=o.getControls("customCss"),t=o.getControls("copyPasteJsonValue");e&&t&&(t.attr("spellcheck","false"),e.attr("spellcheck","false"))}};return l}
|
||||
+367
@@ -0,0 +1,367 @@
|
||||
/**
|
||||
* WPForms Form Builder Themes: Background module.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @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 max-lines-per-function
|
||||
const WPForms = window.WPForms || {};
|
||||
const WPFormsBuilderThemes = WPForms.Admin.Builder.Themes || {};
|
||||
|
||||
/**
|
||||
* Elements holder.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const el = {};
|
||||
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
const app = {
|
||||
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
init() {
|
||||
app.setup();
|
||||
app.events();
|
||||
|
||||
WPFormsBuilderThemes.store.subscribe( 'backgroundUrl', ( value ) => {
|
||||
app.setImagePreview( value );
|
||||
app.maybeShowChooseButton();
|
||||
} );
|
||||
|
||||
WPFormsBuilderThemes.store.subscribe( 'backgroundImage', ( value ) => {
|
||||
app.maybeShowImageSelector( value );
|
||||
app.maybeShowChooseButton();
|
||||
} );
|
||||
|
||||
WPFormsBuilderThemes.store.subscribe( 'backgroundSizeMode', ( value ) => {
|
||||
app.handleSizeFromDimensions( value );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
setup() {
|
||||
el.$builder = $( '#wpforms-builder' );
|
||||
el.$preview = $( '#wpforms-builder-themes-preview' );
|
||||
el.$imageSelector = $( '.wpforms-builder-themes-background-selector' );
|
||||
el.$imagePreview = el.$imageSelector.find( '.wpforms-builder-themes-bg-image-preview' );
|
||||
el.$chooseButton = el.$imageSelector.find( '.wpforms-builder-themes-bg-image-choose' );
|
||||
el.$removeButton = el.$imageSelector.find( '.wpforms-builder-themes-bg-image-remove' );
|
||||
|
||||
app.initImageSelector();
|
||||
},
|
||||
|
||||
/**
|
||||
* Events.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
events() {
|
||||
el.$builder
|
||||
.on( 'click', '.wpforms-builder-themes-bg-image-remove', app.removeImage )
|
||||
.on( 'click', '.wpforms-builder-themes-bg-image-choose, .wpforms-builder-themes-bg-image-preview', app.chooseImage );
|
||||
},
|
||||
|
||||
/**
|
||||
* Init the Image Selector control.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
initImageSelector() {
|
||||
const settings = WPFormsBuilderThemes.getSettings();
|
||||
|
||||
el.$imageSelector.removeClass( 'wpforms-hidden' );
|
||||
|
||||
app.setImagePreview( settings.backgroundUrl );
|
||||
app.maybeShowChooseButton();
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle image selector control state.
|
||||
*
|
||||
* @since 1.9.7
|
||||
* @param {string} value `backgroundImage` setting value.
|
||||
*/
|
||||
maybeShowImageSelector( value ) {
|
||||
if ( value === 'none' ) {
|
||||
el.$imageSelector.addClass( 'wpforms-hidden' );
|
||||
} else {
|
||||
el.$imageSelector.removeClass( 'wpforms-hidden' );
|
||||
|
||||
const backgroundUrl = WPFormsBuilderThemes.store.get( 'backgroundUrl' );
|
||||
|
||||
// Here we need to clean the url value and set a new one.
|
||||
// Otherwise, the picture preview won't be updated.
|
||||
WPFormsBuilderThemes.store.set( 'backgroundUrl', 'url()' );
|
||||
WPFormsBuilderThemes.store.set( 'backgroundUrl', backgroundUrl );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove an image button handler.
|
||||
*
|
||||
* @param {Object} e Event object
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
removeImage( e ) {
|
||||
e.preventDefault();
|
||||
WPFormsBuilderThemes.store.set( 'backgroundUrl', 'url()' );
|
||||
el.$chooseButton.removeClass( 'wpforms-hidden' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Choose an image button handler.
|
||||
*
|
||||
* @param {Object} e Event object
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
chooseImage( e ) {
|
||||
e.preventDefault();
|
||||
const settings = WPFormsBuilderThemes.getSettings();
|
||||
|
||||
if ( settings.backgroundImage === 'library' ) {
|
||||
app.openMediaLibrary();
|
||||
} else {
|
||||
WPFormsBuilderThemes.stockPhotos.openModal( 'bg-styles' );
|
||||
}
|
||||
|
||||
app.maybeShowChooseButton();
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the image preview.
|
||||
*
|
||||
* @param {null|string} value Image preview url value.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
setImagePreview( value = null ) {
|
||||
const isHidden = ! value || value === 'url()';
|
||||
const imageValue = isHidden ? 'url()' : `url(${ value })`;
|
||||
|
||||
el.$imagePreview.css( 'background-image', imageValue );
|
||||
el.$imagePreview.toggleClass( 'wpforms-hidden', isHidden );
|
||||
el.$removeButton.toggleClass( 'wpforms-hidden', isHidden );
|
||||
},
|
||||
|
||||
/**
|
||||
* Conditionally show or hide the `Choose Image` button.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
maybeShowChooseButton() {
|
||||
const settings = WPFormsBuilderThemes.getSettings();
|
||||
|
||||
if ( settings.backgroundImage !== 'none' && settings.backgroundUrl === 'url()' ) {
|
||||
el.$chooseButton.removeClass( 'wpforms-hidden' );
|
||||
} else {
|
||||
el.$chooseButton.addClass( 'wpforms-hidden' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Open media library modal and handle image selection.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
openMediaLibrary() {
|
||||
const frame = wp.media( {
|
||||
multiple: false,
|
||||
library: {
|
||||
type: 'image',
|
||||
},
|
||||
} );
|
||||
|
||||
frame.on( 'select', () => {
|
||||
const attachment = frame.state().get( 'selection' ).first().toJSON();
|
||||
|
||||
if ( attachment.url ) {
|
||||
WPFormsBuilderThemes.store.set( 'backgroundUrl', attachment.url );
|
||||
}
|
||||
} );
|
||||
|
||||
frame.open();
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle the real size from image dimensions.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} value Value.
|
||||
*/
|
||||
handleSizeFromDimensions( value ) {
|
||||
const settings = WPFormsBuilderThemes.getSettings();
|
||||
const $container = el.$preview.find( '.wpforms-container' )[ 0 ];
|
||||
const backgroundWidth = WPFormsBuilderThemes.common.prepareComplexAttrValues( settings.backgroundWidth, 'backgroundWidth' );
|
||||
const backgroundHeight = WPFormsBuilderThemes.common.prepareComplexAttrValues( settings.backgroundHeight, 'backgroundHeight' );
|
||||
const $backgroundSizeControl = WPFormsBuilderThemes.getControls( 'backgroundSize' );
|
||||
|
||||
if ( value === 'cover' ) {
|
||||
app.setContainerBackgroundWidth( $container, backgroundWidth );
|
||||
app.setContainerBackgroundHeight( $container, backgroundHeight );
|
||||
|
||||
$container.style.setProperty( `--wpforms-background-size`, 'cover' );
|
||||
$backgroundSizeControl.val( 'cover' );
|
||||
} else {
|
||||
$container.style.setProperty( `--wpforms-background-size`, backgroundWidth + ' ' + backgroundHeight );
|
||||
$backgroundSizeControl.val( backgroundWidth + ' ' + backgroundHeight );
|
||||
}
|
||||
|
||||
$backgroundSizeControl.trigger( 'input' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle real size from height.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {HTMLElement} container Form preview container
|
||||
* @param {string} value Value.
|
||||
* @param {Object} atts Form style settings.
|
||||
*/
|
||||
handleSizeFromHeight( container, value, atts ) {
|
||||
const backgroundWidth = WPFormsBuilderThemes.common.prepareComplexAttrValues( atts.backgroundWidth, 'backgroundWidth' );
|
||||
const $backgroundSizeControl = WPFormsBuilderThemes.getControls( 'backgroundSize' );
|
||||
|
||||
app.setContainerBackgroundHeight( container, value );
|
||||
|
||||
if ( atts.backgroundSizeMode !== 'cover' ) {
|
||||
$backgroundSizeControl.val( backgroundWidth + ' ' + value );
|
||||
container.style.setProperty( `--wpforms-background-size`, backgroundWidth + ' ' + value );
|
||||
$backgroundSizeControl.trigger( 'input' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle real size from width.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {HTMLElement} container Form preview container
|
||||
* @param {string} value Value.
|
||||
* @param {Object} atts Form style settings.
|
||||
*/
|
||||
handleSizeFromWidth( container, value, atts ) {
|
||||
const backgroundWidth = WPFormsBuilderThemes.common.prepareComplexAttrValues( atts.backgroundWidth, 'backgroundWidth' );
|
||||
const backgroundHeight = WPFormsBuilderThemes.common.prepareComplexAttrValues( atts.backgroundHeight, 'backgroundHeight' );
|
||||
const $backgroundSizeControl = WPFormsBuilderThemes.getControls( 'backgroundSize' );
|
||||
|
||||
app.setContainerBackgroundWidth( container, backgroundWidth );
|
||||
|
||||
if ( atts.backgroundSizeMode !== 'cover' ) {
|
||||
$backgroundSizeControl.val( value + ' ' + backgroundHeight );
|
||||
container.style.setProperty( `--wpforms-background-size`, value + ' ' + backgroundHeight );
|
||||
$backgroundSizeControl.trigger( 'input' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the container background color.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {HTMLElement} container Container element.
|
||||
* @param {string} value Value.
|
||||
*/
|
||||
setBackgroundColor( container, value ) {
|
||||
container.style.setProperty( `--wpforms-background-color`, value );
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the container background url.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {HTMLElement} container Container element.
|
||||
* @param {string} value Value.
|
||||
*/
|
||||
setBackgroundUrl( container, value ) {
|
||||
container.style.setProperty( `--wpforms-background-url`, value );
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the container background height.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {HTMLElement} container Container element.
|
||||
* @param {string} value Value.
|
||||
*/
|
||||
setContainerBackgroundHeight( container, value ) {
|
||||
container.style.setProperty( `--wpforms-background-height`, value );
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the container background image.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {HTMLElement} container Container element.
|
||||
* @param {string} value Value.
|
||||
*/
|
||||
setContainerBackgroundImage( container, value ) {
|
||||
if ( value === 'none' ) {
|
||||
container.style.setProperty( `--wpforms-background-url`, 'url()' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the container background position.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {HTMLElement} container Container element.
|
||||
* @param {string} value Value.
|
||||
*/
|
||||
setContainerBackgroundPosition( container, value ) {
|
||||
container.style.setProperty( `--wpforms-background-position`, value );
|
||||
},
|
||||
|
||||
/**
|
||||
* Set container background repeat.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {HTMLElement} container Container element.
|
||||
* @param {string} value Value.
|
||||
*/
|
||||
setContainerBackgroundRepeat( container, value ) {
|
||||
container.style.setProperty( `--wpforms-background-repeat`, value );
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the container background width.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {HTMLElement} container Container element.
|
||||
* @param {string} value Value.
|
||||
*/
|
||||
setContainerBackgroundWidth( container, value ) {
|
||||
container.style.setProperty( `--wpforms-background-width`, value );
|
||||
},
|
||||
};
|
||||
|
||||
return app;
|
||||
}
|
||||
Vendored
Executable
+1
@@ -0,0 +1 @@
|
||||
export default function(e,r,o){let n=(r.WPForms||{}).Admin.Builder.Themes||{},a={},i={init(){i.setup(),i.events(),n.store.subscribe("backgroundUrl",e=>{i.setImagePreview(e),i.maybeShowChooseButton()}),n.store.subscribe("backgroundImage",e=>{i.maybeShowImageSelector(e),i.maybeShowChooseButton()}),n.store.subscribe("backgroundSizeMode",e=>{i.handleSizeFromDimensions(e)})},setup(){a.$builder=o("#wpforms-builder"),a.$preview=o("#wpforms-builder-themes-preview"),a.$imageSelector=o(".wpforms-builder-themes-background-selector"),a.$imagePreview=a.$imageSelector.find(".wpforms-builder-themes-bg-image-preview"),a.$chooseButton=a.$imageSelector.find(".wpforms-builder-themes-bg-image-choose"),a.$removeButton=a.$imageSelector.find(".wpforms-builder-themes-bg-image-remove"),i.initImageSelector()},events(){a.$builder.on("click",".wpforms-builder-themes-bg-image-remove",i.removeImage).on("click",".wpforms-builder-themes-bg-image-choose, .wpforms-builder-themes-bg-image-preview",i.chooseImage)},initImageSelector(){var e=n.getSettings();a.$imageSelector.removeClass("wpforms-hidden"),i.setImagePreview(e.backgroundUrl),i.maybeShowChooseButton()},maybeShowImageSelector(e){"none"===e?a.$imageSelector.addClass("wpforms-hidden"):(a.$imageSelector.removeClass("wpforms-hidden"),e=n.store.get("backgroundUrl"),n.store.set("backgroundUrl","url()"),n.store.set("backgroundUrl",e))},removeImage(e){e.preventDefault(),n.store.set("backgroundUrl","url()"),a.$chooseButton.removeClass("wpforms-hidden")},chooseImage(e){e.preventDefault(),"library"===n.getSettings().backgroundImage?i.openMediaLibrary():n.stockPhotos.openModal("bg-styles"),i.maybeShowChooseButton()},setImagePreview(e=null){var r=!e||"url()"===e,e=r?"url()":`url(${e})`;a.$imagePreview.css("background-image",e),a.$imagePreview.toggleClass("wpforms-hidden",r),a.$removeButton.toggleClass("wpforms-hidden",r)},maybeShowChooseButton(){var e=n.getSettings();"none"!==e.backgroundImage&&"url()"===e.backgroundUrl?a.$chooseButton.removeClass("wpforms-hidden"):a.$chooseButton.addClass("wpforms-hidden")},openMediaLibrary(){let r=wp.media({multiple:!1,library:{type:"image"}});r.on("select",()=>{var e=r.state().get("selection").first().toJSON();e.url&&n.store.set("backgroundUrl",e.url)}),r.open()},handleSizeFromDimensions(e){var r=n.getSettings(),o=a.$preview.find(".wpforms-container")[0],t=n.common.prepareComplexAttrValues(r.backgroundWidth,"backgroundWidth"),r=n.common.prepareComplexAttrValues(r.backgroundHeight,"backgroundHeight"),s=n.getControls("backgroundSize");"cover"===e?(i.setContainerBackgroundWidth(o,t),i.setContainerBackgroundHeight(o,r),o.style.setProperty("--wpforms-background-size","cover"),s.val("cover")):(o.style.setProperty("--wpforms-background-size",t+" "+r),s.val(t+" "+r)),s.trigger("input")},handleSizeFromHeight(e,r,o){var t=n.common.prepareComplexAttrValues(o.backgroundWidth,"backgroundWidth"),s=n.getControls("backgroundSize");i.setContainerBackgroundHeight(e,r),"cover"!==o.backgroundSizeMode&&(s.val(t+" "+r),e.style.setProperty("--wpforms-background-size",t+" "+r),s.trigger("input"))},handleSizeFromWidth(e,r,o){var t=n.common.prepareComplexAttrValues(o.backgroundWidth,"backgroundWidth"),s=n.common.prepareComplexAttrValues(o.backgroundHeight,"backgroundHeight"),a=n.getControls("backgroundSize");i.setContainerBackgroundWidth(e,t),"cover"!==o.backgroundSizeMode&&(a.val(r+" "+s),e.style.setProperty("--wpforms-background-size",r+" "+s),a.trigger("input"))},setBackgroundColor(e,r){e.style.setProperty("--wpforms-background-color",r)},setBackgroundUrl(e,r){e.style.setProperty("--wpforms-background-url",r)},setContainerBackgroundHeight(e,r){e.style.setProperty("--wpforms-background-height",r)},setContainerBackgroundImage(e,r){"none"===r&&e.style.setProperty("--wpforms-background-url","url()")},setContainerBackgroundPosition(e,r){e.style.setProperty("--wpforms-background-position",r)},setContainerBackgroundRepeat(e,r){e.style.setProperty("--wpforms-background-repeat",r)},setContainerBackgroundWidth(e,r){e.style.setProperty("--wpforms-background-width",r)}};return i}
|
||||
+955
@@ -0,0 +1,955 @@
|
||||
/* global wpf, wpforms_builder_themes, WPFormsBuilder, wpforms_education, WPFormsEducation, WPFormsUtils */
|
||||
|
||||
/**
|
||||
* WPForms Form Builder Themes: Common module.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @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 max-lines-per-function
|
||||
const WPForms = window.WPForms || {};
|
||||
const WPFormsBuilderThemes = WPForms.Admin.Builder.Themes || {};
|
||||
|
||||
/**
|
||||
* Localized data aliases.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
const { isPro, isLicenseActive, isModern, isFullStyles, isLowFormPagesVersion, strings } = wpforms_builder_themes;
|
||||
|
||||
/**
|
||||
* Elements holder.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const el = {};
|
||||
|
||||
/**
|
||||
* Field dependencies configuration.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const fieldDependencies = {
|
||||
fieldBorderStyle: {
|
||||
none: {
|
||||
disable: [ 'fieldBorderSize', 'fieldBorderColor' ],
|
||||
},
|
||||
},
|
||||
buttonBorderStyle: {
|
||||
none: {
|
||||
disable: [ 'buttonBorderSize', 'buttonBorderColor' ],
|
||||
},
|
||||
},
|
||||
containerBorderStyle: {
|
||||
none: {
|
||||
disable: [ 'containerBorderWidth', 'containerBorderColor' ],
|
||||
},
|
||||
},
|
||||
backgroundImage: {
|
||||
none: {
|
||||
hide: [ 'backgroundPosition', 'backgroundRepeat', 'backgroundSizeMode', 'backgroundWidth', 'backgroundHeight' ],
|
||||
},
|
||||
},
|
||||
backgroundSizeMode: {
|
||||
cover: {
|
||||
hide: [ 'backgroundWidth', 'backgroundHeight' ],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
const app = {
|
||||
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
init() {
|
||||
app.setup();
|
||||
app.events();
|
||||
|
||||
// Maybe show the sidebar after page reload.
|
||||
app.handlePanelSwitch();
|
||||
|
||||
// Init color pickers.
|
||||
app.loadColorPickers();
|
||||
|
||||
// Init settings store.
|
||||
WPFormsBuilderThemes.store.initFromDOM();
|
||||
|
||||
// Subscribe to all settings change.
|
||||
WPFormsBuilderThemes.store.subscribeAll( ( value, key ) => {
|
||||
app.changeStyleSettings( value, key );
|
||||
app.handleFieldDependencies( key, value );
|
||||
} );
|
||||
|
||||
// Render already saved settings.
|
||||
app.renderSavedSettings();
|
||||
|
||||
// Apply initial dependencies.
|
||||
app.applyAllDependencies();
|
||||
|
||||
// Block PRO controls.
|
||||
app.blockProSections();
|
||||
|
||||
// Run checks.
|
||||
app.runChecks();
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
setup() {
|
||||
el.$builder = $( '#wpforms-builder' );
|
||||
el.$settings = $( '.wpforms-panel-content-section-themes' );
|
||||
el.$sidebar = $( '#wpforms-builder-themes-sidebar' );
|
||||
el.$preview = $( '#wpforms-builder-themes-preview' );
|
||||
el.$tabs = $( '#wpforms-builder-themes-sidebar-tabs > a' );
|
||||
|
||||
// Set the custom class to sidebar content for macOS.
|
||||
if ( app.isMac() ) {
|
||||
el.$sidebar.find( '.wpforms-builder-themes-sidebar-content' ).addClass( 'wpforms-is-mac' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
events() {
|
||||
el.$builder
|
||||
.on( 'click', '#wpforms-builder-themes-back', app.handleClosePreviewSidebar )
|
||||
.on( 'click', '.wpforms-panel-sidebar-section-themes', app.handleOpenPreviewSidebar )
|
||||
.on( 'wpformsPanelSwitched', '.wpforms-panel-sidebar-section-themes', app.handlePanelSwitch )
|
||||
.on( 'wpformsPanelSectionSwitch', app.handlePanelSectionSwitch )
|
||||
.on( 'click', '.wpforms-panel-settings-button.active[data-panel="settings"]', app.handleSettingsTabClick );
|
||||
|
||||
el.$tabs.on( 'click', app.handleTabClick );
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle sidebar closing when the 'Settings' tab button is clicked.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
handleSettingsTabClick() {
|
||||
if ( el.$sidebar.hasClass( 'wpforms-hidden' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
app.handleClosePreviewSidebar( null );
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle field dependencies when a field value changes.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} fieldKey The field key that changed.
|
||||
* @param {string} fieldValue The new field value.
|
||||
*/
|
||||
handleFieldDependencies( fieldKey, fieldValue ) {
|
||||
// After handling the specific field dependency, re-apply all dependencies
|
||||
// to ensure all conditions are properly evaluated with current values.
|
||||
app.applyFieldDependency( fieldKey, fieldValue );
|
||||
app.applyAllDependencies();
|
||||
},
|
||||
|
||||
/**
|
||||
* Apply dependency for a specific field.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} fieldKey The field key that changed.
|
||||
* @param {string} fieldValue The new field value.
|
||||
*/
|
||||
applyFieldDependency( fieldKey, fieldValue ) {
|
||||
if ( ! fieldDependencies[ fieldKey ] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dependencies = fieldDependencies[ fieldKey ];
|
||||
|
||||
// Check each condition for the field.
|
||||
// eslint-disable-next-line complexity
|
||||
Object.keys( dependencies ).forEach( ( conditionValue ) => {
|
||||
const condition = dependencies[ conditionValue ];
|
||||
const shouldApply = fieldValue === conditionValue;
|
||||
|
||||
// Handle disable conditions.
|
||||
if ( condition.disable && shouldApply ) {
|
||||
condition.disable.forEach( ( dependentField ) => {
|
||||
app.disableField( dependentField );
|
||||
} );
|
||||
} else if ( condition.disable ) {
|
||||
condition.disable.forEach( ( dependentField ) => {
|
||||
app.enableField( dependentField );
|
||||
} );
|
||||
}
|
||||
|
||||
// Handle enable conditions.
|
||||
if ( condition.enable && shouldApply ) {
|
||||
condition.enable.forEach( ( dependentField ) => {
|
||||
app.enableField( dependentField );
|
||||
} );
|
||||
} else if ( condition.enable ) {
|
||||
condition.enable.forEach( ( dependentField ) => {
|
||||
app.disableField( dependentField );
|
||||
} );
|
||||
}
|
||||
|
||||
// Handle hide conditions.
|
||||
if ( condition.hide && shouldApply ) {
|
||||
condition.hide.forEach( ( dependentField ) => {
|
||||
app.hideField( dependentField );
|
||||
} );
|
||||
} else if ( condition.hide ) {
|
||||
condition.hide.forEach( ( dependentField ) => {
|
||||
app.showField( dependentField );
|
||||
} );
|
||||
}
|
||||
|
||||
// Handle show conditions.
|
||||
if ( condition.show && shouldApply ) {
|
||||
condition.show.forEach( ( dependentField ) => {
|
||||
app.showField( dependentField );
|
||||
} );
|
||||
} else if ( condition.show ) {
|
||||
condition.show.forEach( ( dependentField ) => {
|
||||
app.hideField( dependentField );
|
||||
} );
|
||||
}
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Apply all dependencies based on current settings.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
applyAllDependencies() {
|
||||
const settings = WPFormsBuilderThemes.getSettings();
|
||||
|
||||
Object.keys( fieldDependencies ).forEach( ( fieldKey ) => {
|
||||
const fieldValue = settings[ fieldKey ];
|
||||
if ( fieldValue !== undefined ) {
|
||||
app.applyFieldDependency( fieldKey, fieldValue );
|
||||
}
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Disable a field and its wrapper.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} fieldKey The field key to disable.
|
||||
*/
|
||||
disableField( fieldKey ) {
|
||||
const $field = el.$sidebar.find( `[name*="${ fieldKey }"]` );
|
||||
|
||||
if ( $field.length ) {
|
||||
$field.addClass( 'wpforms-builder-themes-disabled' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Enable a field and its wrapper.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} fieldKey The field key to enable.
|
||||
*/
|
||||
enableField( fieldKey ) {
|
||||
const $field = el.$sidebar.find( `[name*="${ fieldKey }"]` );
|
||||
|
||||
if ( $field.length ) {
|
||||
$field.removeClass( 'wpforms-builder-themes-disabled' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide a field and its wrapper.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} fieldKey The field key to hide.
|
||||
*/
|
||||
hideField( fieldKey ) {
|
||||
const $field = el.$sidebar.find( `[name*="${ fieldKey }"]` );
|
||||
const $wrapper = $field.parent().parent().hasClass( 'wpforms-builder-themes-conditional-hide' )
|
||||
? $field.parent().parent()
|
||||
: $field.parent( '.wpforms-panel-field' );
|
||||
|
||||
if ( $field.length ) {
|
||||
$field.prop( 'disabled', true );
|
||||
$wrapper.addClass( 'wpforms-builder-themes-hidden' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Show a field and its wrapper.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} fieldKey The field key to show.
|
||||
*/
|
||||
showField( fieldKey ) {
|
||||
const $field = el.$sidebar.find( `[name*="${ fieldKey }"]` );
|
||||
const $wrapper = $field.parent().parent().hasClass( 'wpforms-builder-themes-conditional-hide' )
|
||||
? $field.parent().parent()
|
||||
: $field.parent( '.wpforms-panel-field' );
|
||||
|
||||
if ( $field.length ) {
|
||||
$field.prop( 'disabled', false );
|
||||
$wrapper.removeClass( 'wpforms-builder-themes-hidden' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle opening the custom settings sidebar.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {Object} event The event object.
|
||||
*/
|
||||
handleOpenPreviewSidebar( event ) {
|
||||
el.$sidebar?.removeClass( 'wpforms-hidden' );
|
||||
event?.preventDefault();
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle closing the custom settings sidebar.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {Object} event The event object.
|
||||
*/
|
||||
handleClosePreviewSidebar( event ) {
|
||||
el.$sidebar?.addClass( 'wpforms-hidden' );
|
||||
event?.preventDefault();
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle panel switch and maybe open the sidebar.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
handlePanelSwitch() {
|
||||
if ( wpf.getQueryString( 'section' ) === 'themes' ) {
|
||||
app.handleOpenPreviewSidebar( null );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle panel section switch and maybe open the sidebar.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {Object} _event The event object.
|
||||
* @param {string} section The section that was switched to.
|
||||
*/
|
||||
handlePanelSectionSwitch( _event, section ) {
|
||||
if ( section === 'themes' ) {
|
||||
app.checkForFormFeatures();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle tabs click.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {Object} e Event object.
|
||||
*/
|
||||
handleTabClick( e ) {
|
||||
e.preventDefault();
|
||||
el.$tabs.toggleClass( 'active' );
|
||||
$( '.wpforms-builder-themes-sidebar-tab-content' ).toggleClass( 'wpforms-hidden' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a list of the style settings keys.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @return {Array} Settings keys
|
||||
*/
|
||||
getStyleAttributesKeys() {
|
||||
return [
|
||||
'containerPadding',
|
||||
'containerBorderStyle',
|
||||
'containerBorderWidth',
|
||||
'containerBorderRadius',
|
||||
'containerShadowSize',
|
||||
'containerBorderColor',
|
||||
'fieldSize',
|
||||
'fieldBorderStyle',
|
||||
'fieldBorderRadius',
|
||||
'fieldBorderSize',
|
||||
'fieldBackgroundColor',
|
||||
'fieldBorderColor',
|
||||
'fieldTextColor',
|
||||
'fieldMenuColor',
|
||||
'pageBreakColor',
|
||||
'labelSize',
|
||||
'labelColor',
|
||||
'labelSublabelColor',
|
||||
'labelErrorColor',
|
||||
'buttonSize',
|
||||
'buttonBorderStyle',
|
||||
'buttonBorderSize',
|
||||
'buttonBorderRadius',
|
||||
'buttonBackgroundColor',
|
||||
'buttonBorderColor',
|
||||
'buttonTextColor',
|
||||
'backgroundColor',
|
||||
'backgroundPosition',
|
||||
'backgroundUrl',
|
||||
'backgroundRepeat',
|
||||
'backgroundSize',
|
||||
'backgroundSizeMode',
|
||||
'backgroundWidth',
|
||||
'backgroundHeight',
|
||||
'backgroundImage',
|
||||
];
|
||||
},
|
||||
|
||||
/**
|
||||
* Get style handlers.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @return {Object} Style handlers.
|
||||
*/
|
||||
getStyleHandlers() {
|
||||
return {
|
||||
'background-url': WPFormsBuilderThemes.background.setBackgroundUrl,
|
||||
'background-image': WPFormsBuilderThemes.background.setContainerBackgroundImage,
|
||||
'background-position': WPFormsBuilderThemes.background.setContainerBackgroundPosition,
|
||||
'background-repeat': WPFormsBuilderThemes.background.setContainerBackgroundRepeat,
|
||||
'background-color': WPFormsBuilderThemes.background.setBackgroundColor,
|
||||
'background-height': WPFormsBuilderThemes.background.handleSizeFromHeight,
|
||||
'background-width': WPFormsBuilderThemes.background.handleSizeFromWidth,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Change style setting handler.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} settingValue Setting value.
|
||||
* @param {string} settingKey Setting key.
|
||||
*/
|
||||
changeStyleSettings( settingValue, settingKey ) {// eslint-disable-line complexity
|
||||
const wpformsContainer = el.$preview.find( '.wpforms-container' )[ 0 ];
|
||||
|
||||
if ( ! wpformsContainer ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Process only styles related settings
|
||||
if ( ! app.getStyleAttributesKeys().includes( settingKey ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const settings = WPFormsBuilderThemes.getSettings();
|
||||
|
||||
/**
|
||||
* @type {Object}
|
||||
*/
|
||||
const property = settingKey.replace( /[A-Z]/g, ( letter ) => `-${ letter.toLowerCase() }` );
|
||||
settingValue = app.prepareComplexAttrValues( settingValue, settingKey );
|
||||
|
||||
// Check for custom handlers.
|
||||
if ( typeof app.getStyleHandlers()[ property ] === 'function' ) {
|
||||
app.getStyleHandlers()[ property ]( wpformsContainer, settingValue, settings );
|
||||
return;
|
||||
}
|
||||
|
||||
switch ( property ) {
|
||||
case 'field-size':
|
||||
case 'label-size':
|
||||
case 'button-size':
|
||||
case 'container-shadow-size':
|
||||
for ( const key in wpforms_builder_themes.sizes[ property ][ settingValue ] ) {
|
||||
wpformsContainer.style.setProperty(
|
||||
`--wpforms-${ property }-${ key }`,
|
||||
wpforms_builder_themes.sizes[ property ][ settingValue ][ key ],
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'button-background-color':
|
||||
app.maybeUpdateAccentColor( settings.buttonBorderColor, settingValue, wpformsContainer );
|
||||
settingValue = app.maybeSetButtonAltBackgroundColor( settingValue, settings.buttonBorderColor, wpformsContainer );
|
||||
app.maybeSetButtonAltTextColor( settings.buttonTextColor, settingValue, settings.buttonBorderColor, wpformsContainer );
|
||||
wpformsContainer.style.setProperty( `--wpforms-${ property }`, settingValue );
|
||||
|
||||
break;
|
||||
|
||||
case 'button-border-color':
|
||||
app.maybeUpdateAccentColor( settingValue, settings.buttonBackgroundColor, wpformsContainer );
|
||||
app.maybeSetButtonAltTextColor( settings.buttonTextColor, settings.buttonBackgroundColor, settingValue, wpformsContainer );
|
||||
wpformsContainer.style.setProperty( `--wpforms-${ property }`, settingValue );
|
||||
|
||||
break;
|
||||
|
||||
case 'button-text-color':
|
||||
app.maybeSetButtonAltTextColor( settingValue, settings.buttonBackgroundColor, settings.buttonBorderColor, wpformsContainer );
|
||||
wpformsContainer.style.setProperty( `--wpforms-${ property }`, settingValue );
|
||||
|
||||
break;
|
||||
default:
|
||||
wpformsContainer.style.setProperty( `--wpforms-${ property }`, settingValue );
|
||||
wpformsContainer.style.setProperty( `--wpforms-${ property }-spare`, settingValue );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Maybe update accent color.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} color Color value.
|
||||
* @param {string} buttonBackgroundColor Button background color.
|
||||
* @param {Object} container Form container.
|
||||
*/
|
||||
maybeUpdateAccentColor( color, buttonBackgroundColor, container ) {
|
||||
// Setting the CSS property value to the child element overrides the parent property value.
|
||||
const formWrapper = container.querySelector( '#builder-themes-form-preview-wrapper' );
|
||||
|
||||
// Fallback to the default color if the border color is transparent.
|
||||
color = WPFormsUtils.cssColorsUtils.isTransparentColor( color ) ? '#066aab' : color;
|
||||
|
||||
if ( WPFormsUtils.cssColorsUtils.isTransparentColor( buttonBackgroundColor ) ) {
|
||||
formWrapper.style.setProperty( '--wpforms-button-background-color-alt', 'rgba( 0, 0, 0, 0 )' );
|
||||
formWrapper.style.setProperty( '--wpforms-button-background-color', color );
|
||||
} else {
|
||||
container.style.setProperty( '--wpforms-button-background-color-alt', buttonBackgroundColor );
|
||||
formWrapper.style.setProperty( '--wpforms-button-background-color-alt', null );
|
||||
formWrapper.style.setProperty( '--wpforms-button-background-color', null );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Maybe set the button's alternative background color.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} value Setting value.
|
||||
* @param {string} buttonBorderColor Button border color.
|
||||
* @param {Object} container Form container.
|
||||
*
|
||||
* @return {string|*} New background color.
|
||||
*/
|
||||
maybeSetButtonAltBackgroundColor( value, buttonBorderColor, container ) {
|
||||
// Setting the CSS property value to the child element overrides the parent property value.
|
||||
const formWrapper = container.querySelector( '#builder-themes-form-preview-wrapper' );
|
||||
|
||||
formWrapper.style.setProperty( '--wpforms-button-background-color-alt', value );
|
||||
|
||||
if ( WPFormsUtils.cssColorsUtils.isTransparentColor( value ) ) {
|
||||
return WPFormsUtils.cssColorsUtils.isTransparentColor( buttonBorderColor ) ? '#066aab' : buttonBorderColor;
|
||||
}
|
||||
|
||||
return value;
|
||||
},
|
||||
|
||||
/**
|
||||
* Maybe set the button's alternative text color.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} value Setting value.
|
||||
* @param {string} buttonBackgroundColor Button background color.
|
||||
* @param {string} buttonBorderColor Button border color.
|
||||
* @param {Object} container Form container.
|
||||
*/
|
||||
maybeSetButtonAltTextColor( value, buttonBackgroundColor, buttonBorderColor, container ) {
|
||||
const formWrapper = container.querySelector( '#builder-themes-form-preview-wrapper' );
|
||||
|
||||
let altColor = null;
|
||||
|
||||
value = value.toLowerCase();
|
||||
|
||||
if (
|
||||
WPFormsUtils.cssColorsUtils.isTransparentColor( value ) ||
|
||||
value === buttonBackgroundColor ||
|
||||
(
|
||||
WPFormsUtils.cssColorsUtils.isTransparentColor( buttonBackgroundColor ) &&
|
||||
value === buttonBorderColor
|
||||
)
|
||||
) {
|
||||
altColor = WPFormsUtils.cssColorsUtils.getContrastColor( buttonBackgroundColor );
|
||||
}
|
||||
|
||||
container.style.setProperty( `--wpforms-button-text-color-alt`, value );
|
||||
formWrapper.style.setProperty( `--wpforms-button-text-color-alt`, altColor );
|
||||
},
|
||||
|
||||
/**
|
||||
* Prepare complex setting values.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string|Object} value Setting value.
|
||||
* @param {string} key Attribute key.
|
||||
*
|
||||
* @return {string|Object} Prepared setting value.
|
||||
*/
|
||||
prepareComplexAttrValues( value, key ) {
|
||||
const pxItems = [
|
||||
'fieldBorderRadius',
|
||||
'fieldBorderSize',
|
||||
'buttonBorderRadius',
|
||||
'buttonBorderSize',
|
||||
'containerPadding',
|
||||
'containerBorderWidth',
|
||||
'containerBorderRadius',
|
||||
'backgroundWidth',
|
||||
'backgroundHeight',
|
||||
];
|
||||
|
||||
if ( pxItems.includes( key ) ) {
|
||||
if ( typeof value === 'number' || ( typeof value === 'string' && ! value.trim().endsWith( 'px' ) ) ) {
|
||||
value = `${ value }px`;
|
||||
}
|
||||
}
|
||||
|
||||
if ( key === 'backgroundUrl' ) {
|
||||
if ( typeof value === 'string' && ! value.trim().startsWith( 'url(' ) ) {
|
||||
value = value ? `url( ${ value } )` : 'url()';
|
||||
}
|
||||
}
|
||||
|
||||
// Remove spaces after/before braces in rgb/rgba colors.
|
||||
value = app.removeRgbaSpaces( value );
|
||||
|
||||
return value;
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove extra spaces in rgba/rgb values.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} value Setting value.
|
||||
*
|
||||
* @return {string} Prepared setting value.
|
||||
*/
|
||||
removeRgbaSpaces( value ) {
|
||||
if ( typeof value !== 'string' || ! value.includes( 'rgb' ) ) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return value
|
||||
.replace( /\(\s*/g, '(' )
|
||||
.replace( /\s*\)/g, ')' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Render already saved settings.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
renderSavedSettings() {
|
||||
const wpformsContainer = el.$preview.find( '.wpforms-container' )[ 0 ];
|
||||
|
||||
if ( ! wpformsContainer ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const settings = WPFormsBuilderThemes.getSettings();
|
||||
|
||||
_.each( settings, ( value, key ) => {
|
||||
app.changeStyleSettings( value, key );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Custom loader for color pickers.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
loadColorPickers() {
|
||||
WPFormsBuilder.loadColorPickers( el.$sidebar, {
|
||||
position: 'top left',
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Disable PRO sections.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
blockProSections() {
|
||||
if ( isPro && isLicenseActive ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const $proSectionsHeadings = $( '.wpforms-add-fields-heading[data-group="background_styles"], .wpforms-add-fields-heading[data-group="container_styles"]' );
|
||||
const proSections = $( '.wpforms-builder-themes-pro-section' );
|
||||
|
||||
// Disable sections and show the PRO badge.
|
||||
proSections.addClass( 'wpforms-builder-themes-disabled-pro' );
|
||||
$proSectionsHeadings.addClass( 'wpforms-builder-themes-pro-blocked' );
|
||||
|
||||
// Disable clicks on blocked sections.
|
||||
proSections.off( 'click' ).on( 'click', app.handleProSectionClick );
|
||||
},
|
||||
|
||||
/**
|
||||
* Disable all sections.
|
||||
*
|
||||
* @since 1.9.7
|
||||
* @param {boolean} unblock Need to unblock status.
|
||||
*/
|
||||
blockAllSections( unblock = false ) {
|
||||
const sections = el.$sidebar.find( '.wpforms-add-fields-buttons, .wpforms-builder-themes-sidebar-advanced' );
|
||||
|
||||
// Disable/Enable all sections.
|
||||
if ( ! unblock ) {
|
||||
sections.addClass( 'wpforms-builder-themes-disabled' );
|
||||
} else {
|
||||
sections.removeClass( 'wpforms-builder-themes-disabled' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle the PRO section click.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
handleProSectionClick() {
|
||||
const section = $( this ).prev( 'a' ).data( 'group' )?.replace( '_styles', '' );
|
||||
|
||||
if ( ! isPro ) {
|
||||
app.showProModal( section, strings.pro_sections[ section ] );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! isLicenseActive ) {
|
||||
app.showLicenseModal( strings.pro_sections[ section ], strings.pro_sections[ section ], 'pro-section' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the educational popup for users with no Pro license.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} panel Panel slug.
|
||||
* @param {string} feature Feature name.
|
||||
*/
|
||||
showProModal( panel, feature ) {
|
||||
const type = 'pro';
|
||||
const message = wpforms_education.upgrade[ type ].message_plural.replace( /%name%/g, feature );
|
||||
const utmContent = {
|
||||
container: 'General Container Styles',
|
||||
background: 'General Background Styles',
|
||||
themes: 'General Pro Themes',
|
||||
};
|
||||
|
||||
$.alert( {
|
||||
backgroundDismiss: true,
|
||||
title: feature + ' ' + wpforms_education.upgrade[ type ].title_plural,
|
||||
icon: 'fa fa-lock',
|
||||
content: message,
|
||||
boxWidth: '550px',
|
||||
theme: 'modern,wpforms-education',
|
||||
closeIcon: true,
|
||||
onOpenBefore: function() { // eslint-disable-line object-shorthand
|
||||
this.$btnc.after( '<div class="discount-note">' + wpforms_education.upgrade_bonus + '</div>' );
|
||||
this.$btnc.after( wpforms_education.upgrade[ type ].doc.replace( /%25name%25/g, 'AP - ' + feature ) );
|
||||
this.$body.find( '.jconfirm-content' ).addClass( 'lite-upgrade' );
|
||||
},
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: wpforms_education.upgrade[ type ].button,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
action: () => {
|
||||
window.open( WPFormsEducation.core.getUpgradeURL( utmContent[ panel ], type ), '_blank' );
|
||||
WPFormsEducation.core.upgradeModalThankYou( type );
|
||||
},
|
||||
},
|
||||
},
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Open license modal.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} feature Feature name.
|
||||
* @param {string} fieldName Field name.
|
||||
* @param {string} utmContent UTM content.
|
||||
*/
|
||||
showLicenseModal( feature, fieldName, utmContent ) {
|
||||
WPFormsEducation.proCore.licenseModal( feature, fieldName, utmContent );
|
||||
},
|
||||
|
||||
/**
|
||||
* Run custom checks.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
runChecks() {
|
||||
app.checkForClassicStyles();
|
||||
|
||||
if ( isPro && isLicenseActive && isModern && isFullStyles ) {
|
||||
app.checkForFormFeatures();
|
||||
}
|
||||
|
||||
app.checkForOldFP();
|
||||
},
|
||||
|
||||
/**
|
||||
* Conditionally show/hide classic styles notice and block/unblock controls.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
checkForClassicStyles() {
|
||||
const $notice = $( '.wpforms-builder-themes-style-notice' );
|
||||
const $previewNotice = $( '.wpforms-builder-themes-preview-notice' );
|
||||
|
||||
if ( ! isModern || ! isFullStyles ) {
|
||||
app.blockAllSections();
|
||||
$notice.removeClass( 'wpforms-hidden' );
|
||||
$previewNotice.addClass( 'wpforms-hidden' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Check both Lead Forms and Conversational Forms states and update the UI accordingly.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
checkForFormFeatures() {
|
||||
const $LFSwitch = $( '#wpforms-panel-field-lead_forms-enable' );
|
||||
const $CFSwitch = $( '#wpforms-panel-field-settings-conversational_forms_enable' );
|
||||
const isLFEnabled = $LFSwitch.prop( 'checked' ) ?? false;
|
||||
const isCFEnabled = $CFSwitch.prop( 'checked' ) ?? false;
|
||||
const $LFNotice = $( '.wpforms-builder-themes-lf-notice' );
|
||||
const $CFNotice = $( '.wpforms-builder-themes-cf-notice' );
|
||||
const $previewNotice = $( '.wpforms-builder-themes-preview-notice' );
|
||||
|
||||
// Handle Lead Forms notice visibility
|
||||
if ( isLFEnabled ) {
|
||||
$LFNotice.removeClass( 'wpforms-hidden' );
|
||||
} else {
|
||||
$LFNotice.addClass( 'wpforms-hidden' );
|
||||
}
|
||||
|
||||
// Handle Conversational Forms notice visibility
|
||||
if ( isCFEnabled ) {
|
||||
$CFNotice.removeClass( 'wpforms-hidden' );
|
||||
} else {
|
||||
$CFNotice.addClass( 'wpforms-hidden' );
|
||||
}
|
||||
|
||||
// If either feature is enabled, hide preview and block sections
|
||||
if ( isLFEnabled || isCFEnabled ) {
|
||||
$previewNotice.addClass( 'wpforms-hidden' );
|
||||
el.$preview.addClass( 'wpforms-hidden' );
|
||||
app.blockAllSections();
|
||||
} else {
|
||||
// Only if both features are disabled, show preview and unblock sections
|
||||
el.$preview.removeClass( 'wpforms-hidden' );
|
||||
if ( isModern && isFullStyles ) {
|
||||
app.blockAllSections( true );
|
||||
$previewNotice.removeClass( 'wpforms-hidden' );
|
||||
}
|
||||
}
|
||||
|
||||
// Set up event handlers if they haven't been set up yet
|
||||
app.setupFormFeatureEventHandlers();
|
||||
},
|
||||
|
||||
/**
|
||||
* Set up event handlers for Lead Forms and Conversational Forms switches.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
setupFormFeatureEventHandlers() {
|
||||
// Set up notice link handlers
|
||||
$( '.wpforms-builder-themes-lf-notice a' ).off( 'click', app.openLFSettings ).on( 'click', app.openLFSettings );
|
||||
$( '.wpforms-builder-themes-cf-notice a' ).off( 'click', app.openCFSettings ).on( 'click', app.openCFSettings );
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows the notice if the Form Pages addons version is low.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
checkForOldFP() {
|
||||
const $FPContent = $( '#wpforms-form-pages-content-block' );
|
||||
const $notice = $( '#wpforms-page-forms-fbst-notice' );
|
||||
|
||||
if ( $FPContent.length ) {
|
||||
if ( isLowFormPagesVersion ) {
|
||||
$FPContent.prepend( $notice );
|
||||
$notice.removeClass( 'wpforms-hidden' );
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the Lead Forms settings page.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {Object} event Event object.
|
||||
*/
|
||||
openLFSettings( event ) {
|
||||
app.handleClosePreviewSidebar( event );
|
||||
|
||||
$( 'a.wpforms-panel-sidebar-section-lead_forms' ).click();
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the Conversational Forms settings page.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {Object} event Event object.
|
||||
*/
|
||||
openCFSettings( event ) {
|
||||
app.handleClosePreviewSidebar( event );
|
||||
|
||||
$( 'a.wpforms-panel-sidebar-section-conversational_forms' ).click();
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine if the user is on a Mac.
|
||||
*
|
||||
* @return {boolean} True if the user is on a Mac.
|
||||
*/
|
||||
isMac() {
|
||||
return navigator.userAgent.includes( 'Macintosh' );
|
||||
},
|
||||
};
|
||||
|
||||
return app;
|
||||
}
|
||||
Vendored
Executable
+1
File diff suppressed because one or more lines are too long
+356
@@ -0,0 +1,356 @@
|
||||
/* global wpforms_builder_themes */
|
||||
|
||||
/**
|
||||
* WPForms Form Builder Themes: Stock Photos module.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {Object} document Document object.
|
||||
* @param {Object} window Window object.
|
||||
* @param {jQuery} $ jQuery object.
|
||||
*
|
||||
* @return {Object} Public functions and properties.
|
||||
*/
|
||||
// eslint-disable-next-line max-lines-per-function
|
||||
export default function( document, window, $ ) { // eslint-disable-line no-unused-vars
|
||||
const WPForms = window.WPForms || {};
|
||||
const WPFormsBuilderThemes = WPForms.Admin.Builder.Themes || {};
|
||||
|
||||
/**
|
||||
* Localized data aliases.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
const strings = wpforms_builder_themes.strings;
|
||||
const routeNamespace = wpforms_builder_themes.route_namespace;
|
||||
const pictureUrlPath = wpforms_builder_themes.stockPhotos?.urlPath;
|
||||
|
||||
/**
|
||||
* Elements holder.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const el = {};
|
||||
|
||||
/**
|
||||
* Spinner markup.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
const spinner = '<i class="wpforms-loading-spinner wpforms-loading-white wpforms-loading-inline"></i>';
|
||||
|
||||
/**
|
||||
* Runtime state.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const state = {};
|
||||
|
||||
/**
|
||||
* Stock photos pictures' list.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @type {Array}
|
||||
*/
|
||||
let pictures = wpforms_builder_themes.stockPhotos?.pictures;
|
||||
|
||||
/**
|
||||
* Stock photos picture selector markup.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
let picturesMarkup = '';
|
||||
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
const app = {
|
||||
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
init() {
|
||||
app.setup();
|
||||
app.events();
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
setup() {
|
||||
el.$builder = $( '#wpforms-builder' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
events() {
|
||||
},
|
||||
|
||||
/**
|
||||
* Open stock photos modal.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} from From where the modal was triggered, `themes` or `bg-styles`.
|
||||
*/
|
||||
openModal( from ) {
|
||||
if ( app.isPicturesAvailable() ) {
|
||||
app.picturesModal();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
app.installModal( from );
|
||||
},
|
||||
|
||||
/**
|
||||
* Open stock photos install modal on a select theme.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} themeSlug The theme slug.
|
||||
*/
|
||||
onSelectTheme( themeSlug ) {
|
||||
const themesModule = WPFormsBuilderThemes.themes;
|
||||
|
||||
if ( app.isPicturesAvailable() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check only WPForms themes.
|
||||
if ( ! themesModule?.isWPFormsTheme( themeSlug ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const theme = themesModule?.getTheme( themeSlug );
|
||||
const bgUrl = theme.settings?.backgroundUrl;
|
||||
|
||||
if ( bgUrl?.length && bgUrl !== 'url()' ) {
|
||||
app.installModal( 'themes' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Open a modal prompting to download and install the Stock Photos.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} from From where the modal was triggered, `themes` or `bg-styles`.
|
||||
*/
|
||||
installModal( from ) {
|
||||
const installStr = from === 'themes' ? strings.stockInstallTheme : strings.stockInstallBg;
|
||||
|
||||
$.confirm( {
|
||||
title: strings.heads_up,
|
||||
content: installStr + ' ' + strings.stockInstall,
|
||||
icon: 'wpforms-exclamation-circle',
|
||||
type: 'orange wpforms-builder-themes-modal',
|
||||
buttons: {
|
||||
continue: {
|
||||
text: strings.continue,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
action() {
|
||||
// noinspection JSUnresolvedReference
|
||||
this.$$continue.prop( 'disabled', true )
|
||||
.html( spinner + strings.installing );
|
||||
|
||||
// noinspection JSUnresolvedReference
|
||||
this.$$cancel
|
||||
.prop( 'disabled', true );
|
||||
|
||||
app.install( this, from );
|
||||
|
||||
return false;
|
||||
},
|
||||
},
|
||||
cancel: {
|
||||
text: strings.cancel,
|
||||
keys: [ 'esc' ],
|
||||
},
|
||||
},
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Display the modal window with an error message.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} error Error message.
|
||||
*/
|
||||
errorModal( error ) {
|
||||
$.alert( {
|
||||
title: strings.uhoh,
|
||||
content: error || strings.commonError,
|
||||
icon: 'fa fa-exclamation-circle',
|
||||
type: 'red',
|
||||
buttons: {
|
||||
cancel: {
|
||||
text : strings.close,
|
||||
btnClass: 'btn-confirm',
|
||||
keys : [ 'enter' ],
|
||||
},
|
||||
},
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Display the modal window with pictures.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
picturesModal() {
|
||||
state.picturesModal = $.alert( {
|
||||
title : `${ strings.picturesTitle }<p>${ strings.picturesSubTitle }</p>`,
|
||||
content: app.getPictureMarkup(),
|
||||
type: 'picture-selector wpforms-builder-themes-modal',
|
||||
boxWidth: '800px',
|
||||
closeIcon: true,
|
||||
animation: 'opacity',
|
||||
closeAnimation: 'opacity',
|
||||
buttons: false,
|
||||
onOpen() {
|
||||
this.$content
|
||||
.off( 'click' )
|
||||
.on( 'click', '.wpforms-builder-stock-photos-picture', app.selectPicture );
|
||||
},
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Install stock photos.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {Object} modal The jQuery-confirm a modal window object.
|
||||
* @param {string} from From where the modal was triggered, `themes` or `bg-styles`.
|
||||
*/
|
||||
install( modal, from ) {
|
||||
// If a fetch is already in progress, exit the function.
|
||||
if ( state.isInstalling ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the flag to true indicating a fetch is in progress.
|
||||
state.isInstalling = true;
|
||||
|
||||
try {
|
||||
// Fetch themes data.
|
||||
wp.apiFetch( {
|
||||
path: routeNamespace + 'stock-photos/install/',
|
||||
method: 'POST',
|
||||
cache: 'no-cache',
|
||||
} ).then( ( response ) => {
|
||||
if ( ! response.result ) {
|
||||
app.errorModal( response.error );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the pictures' data.
|
||||
pictures = response.pictures || [];
|
||||
|
||||
// Update the theme or open the picture selector modal.
|
||||
if ( from === 'themes' ) {
|
||||
WPFormsBuilderThemes.store.set( 'backgroundUrl', 'url()' );
|
||||
WPFormsBuilderThemes.themes.setFormTheme( WPFormsBuilderThemes.store.get( 'wpformsTheme' ) );
|
||||
} else {
|
||||
app.picturesModal();
|
||||
}
|
||||
} ).catch( ( error ) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error( error?.message );
|
||||
app.errorModal( `<p>${ strings.commonError }</p><p>${ error?.message }</p>` );
|
||||
} ).finally( () => {
|
||||
state.isInstalling = false;
|
||||
|
||||
// Close the modal window.
|
||||
modal.close();
|
||||
} );
|
||||
} catch ( error ) {
|
||||
state.isInstalling = false;
|
||||
// eslint-disable-next-line no-console
|
||||
console.error( error );
|
||||
app.errorModal( strings.commonError + '<br>' + error );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Detect whether pictures' data available.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @return {boolean} True if pictures' data available, false otherwise.
|
||||
*/
|
||||
isPicturesAvailable() {
|
||||
return Boolean( pictures?.length );
|
||||
},
|
||||
|
||||
/**
|
||||
* Generate the pictures' selector markup.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @return {string} Pictures' selector markup.
|
||||
*/
|
||||
getPictureMarkup() {
|
||||
if ( ! app.isPicturesAvailable() ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ( picturesMarkup !== '' ) {
|
||||
return picturesMarkup;
|
||||
}
|
||||
|
||||
pictures.forEach( ( picture ) => {
|
||||
const pictureUrl = pictureUrlPath + picture;
|
||||
|
||||
picturesMarkup += `<div class="wpforms-builder-stock-photos-picture"
|
||||
data-url="${ pictureUrl }"
|
||||
style="background-image: url( '${ pictureUrl }' )"
|
||||
></div>`;
|
||||
} );
|
||||
|
||||
picturesMarkup = `<div class="wpforms-builder-stock-photos-pictures-wrap">${ picturesMarkup }</div>`;
|
||||
|
||||
return picturesMarkup;
|
||||
},
|
||||
|
||||
/**
|
||||
* Select picture event handler.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
selectPicture() {
|
||||
const pictureUrl = $( this ).data( 'url' );
|
||||
|
||||
// Update the settings.
|
||||
WPFormsBuilderThemes.store.set( 'backgroundUrl', pictureUrl );
|
||||
|
||||
// Close the modal window.
|
||||
state.picturesModal?.close();
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
return app;
|
||||
}
|
||||
Vendored
Executable
+4
@@ -0,0 +1,4 @@
|
||||
export default function(e,t,s){let r=(t.WPForms||{}).Admin.Builder.Themes||{},o=wpforms_builder_themes.strings,l=wpforms_builder_themes.route_namespace,i=wpforms_builder_themes.stockPhotos?.urlPath,n={},c={},a=wpforms_builder_themes.stockPhotos?.pictures,u="",p={init(){p.setup(),p.events()},setup(){n.$builder=s("#wpforms-builder")},events(){},openModal(e){p.isPicturesAvailable()?p.picturesModal():p.installModal(e)},onSelectTheme(e){var t=r.themes;p.isPicturesAvailable()||t?.isWPFormsTheme(e)&&(t=(t?.getTheme(e)).settings?.backgroundUrl)?.length&&"url()"!==t&&p.installModal("themes")},installModal(e){var t="themes"===e?o.stockInstallTheme:o.stockInstallBg;s.confirm({title:o.heads_up,content:t+" "+o.stockInstall,icon:"wpforms-exclamation-circle",type:"orange wpforms-builder-themes-modal",buttons:{continue:{text:o.continue,btnClass:"btn-confirm",keys:["enter"],action(){return this.$$continue.prop("disabled",!0).html('<i class="wpforms-loading-spinner wpforms-loading-white wpforms-loading-inline"></i>'+o.installing),this.$$cancel.prop("disabled",!0),p.install(this,e),!1}},cancel:{text:o.cancel,keys:["esc"]}}})},errorModal(e){s.alert({title:o.uhoh,content:e||o.commonError,icon:"fa fa-exclamation-circle",type:"red",buttons:{cancel:{text:o.close,btnClass:"btn-confirm",keys:["enter"]}}})},picturesModal(){c.picturesModal=s.alert({title:`${o.picturesTitle}<p>${o.picturesSubTitle}</p>`,content:p.getPictureMarkup(),type:"picture-selector wpforms-builder-themes-modal",boxWidth:"800px",closeIcon:!0,animation:"opacity",closeAnimation:"opacity",buttons:!1,onOpen(){this.$content.off("click").on("click",".wpforms-builder-stock-photos-picture",p.selectPicture)}})},install(e,t){if(!c.isInstalling){c.isInstalling=!0;try{wp.apiFetch({path:l+"stock-photos/install/",method:"POST",cache:"no-cache"}).then(e=>{e.result?(a=e.pictures||[],"themes"===t?(r.store.set("backgroundUrl","url()"),r.themes.setFormTheme(r.store.get("wpformsTheme"))):p.picturesModal()):p.errorModal(e.error)}).catch(e=>{console.error(e?.message),p.errorModal(`<p>${o.commonError}</p><p>${e?.message}</p>`)}).finally(()=>{c.isInstalling=!1,e.close()})}catch(e){c.isInstalling=!1,console.error(e),p.errorModal(o.commonError+"<br>"+e)}}},isPicturesAvailable(){return Boolean(a?.length)},getPictureMarkup(){return p.isPicturesAvailable()?(""===u&&(a.forEach(e=>{e=i+e;u+=`<div class="wpforms-builder-stock-photos-picture"
|
||||
data-url="${e}"
|
||||
style="background-image: url( '${e}' )"
|
||||
></div>`}),u=`<div class="wpforms-builder-stock-photos-pictures-wrap">${u}</div>`),u):""},selectPicture(){var e=s(this).data("url");r.store.set("backgroundUrl",e),c.picturesModal?.close()}};return p}
|
||||
+908
@@ -0,0 +1,908 @@
|
||||
/* global wpforms_builder_themes */
|
||||
|
||||
/**
|
||||
* WPForms Form Builder Themes: Theme module.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @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 max-lines-per-function
|
||||
const WPForms = window.WPForms || {};
|
||||
const WPFormsBuilderThemes = WPForms.Admin.Builder.Themes || {};
|
||||
|
||||
/**
|
||||
* Localized data aliases.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
const { isAdmin, isPro, isLicenseActive, strings, route_namespace: routeNamespace } = wpforms_builder_themes;
|
||||
|
||||
/**
|
||||
* Runtime state.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const state = {};
|
||||
|
||||
/**
|
||||
* Themes data.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const themesData = {
|
||||
wpforms: null,
|
||||
custom: null,
|
||||
};
|
||||
|
||||
/**
|
||||
* Enabled themes.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
let enabledThemes = null;
|
||||
|
||||
/**
|
||||
* Elements holder.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const el = {};
|
||||
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
const app = {
|
||||
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
init() {
|
||||
app.fetchThemesData();
|
||||
app.setup();
|
||||
app.events();
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
setup() {
|
||||
el.$builder = $( '#wpforms-builder' );
|
||||
el.$themesControl = el.$builder.find( '.wpforms-builder-themes-control' );
|
||||
el.$customThemeRenamer = el.$builder.find( '#wpforms-panel-field-themes-themeName-wrap' );
|
||||
el.$customThemeRemover = el.$builder.find( '#wpforms-builder-themer-remove-theme' );
|
||||
el.$window = $( window );
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
events() {
|
||||
el.$window.on( 'wpformsBuilderThemesDataLoaded', app.themesControlSetup );
|
||||
el.$builder.on( 'wpformsSaved', app.saveCustomThemes );
|
||||
},
|
||||
|
||||
/**
|
||||
* Set up the Themes Select control.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
themesControlSetup() {
|
||||
// Debounce custom themes update and creation.
|
||||
const debouncedMaybeCreate = _.debounce( ( key ) => {
|
||||
app.maybeCreateCustomTheme();
|
||||
app.maybeUpdateCustomTheme( key );
|
||||
}, 300 );
|
||||
|
||||
// Listen for all settings changes.
|
||||
WPFormsBuilderThemes.store.subscribeAll( ( value, key ) => {
|
||||
const allowedKeys = WPFormsBuilderThemes.common.getStyleAttributesKeys();
|
||||
if ( ! allowedKeys.includes( key ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
debouncedMaybeCreate( key );
|
||||
} );
|
||||
|
||||
// Listen for the theme name change.
|
||||
WPFormsBuilderThemes.store.subscribe( 'themeName', ( value ) => {
|
||||
app.changeThemeName( value );
|
||||
app.updateThemesList();
|
||||
} );
|
||||
|
||||
// Listen for the isCustomTheme setting change.
|
||||
WPFormsBuilderThemes.store.subscribe( 'isCustomTheme', () => {
|
||||
app.toggleCustomThemeSettings();
|
||||
} );
|
||||
|
||||
// Check if the selected theme exists. If no, create a new one.
|
||||
app.maybeCreateCustomTheme();
|
||||
|
||||
app.toggleCustomThemeSettings();
|
||||
app.updateThemesList();
|
||||
},
|
||||
|
||||
/**
|
||||
* Update themes list.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
updateThemesList() {
|
||||
const selectedTheme = WPFormsBuilderThemes.store.get( 'wpformsTheme' ) ?? 'default';
|
||||
|
||||
// Get all themes.
|
||||
const html = app.getThemesListMarkup( selectedTheme );
|
||||
|
||||
el.$themesControl.html( html );
|
||||
|
||||
app.addThemesEvents();
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the Themes control markup.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} selectedTheme Selected theme slug.
|
||||
*
|
||||
* @return {string} Themes items HTML.
|
||||
*/
|
||||
getThemesListMarkup( selectedTheme ) {
|
||||
if ( ! themesData.wpforms ) {
|
||||
app.fetchThemesData();
|
||||
|
||||
// Return markup with an error message if themes are not available.
|
||||
return `<div class="wpforms-no-themes">${ strings.themes_error }</div>`;
|
||||
}
|
||||
|
||||
const allThemes = app.getAllThemes();
|
||||
|
||||
if ( ! allThemes ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const themes = Object.keys( allThemes );
|
||||
let theme, firstThemeSlug;
|
||||
let html = '';
|
||||
let itemsHtml = '';
|
||||
|
||||
if ( ! app.isWPFormsTheme( selectedTheme ) ) {
|
||||
firstThemeSlug = selectedTheme;
|
||||
|
||||
itemsHtml += app.getThemesItemMarkup( app.getTheme( firstThemeSlug ), firstThemeSlug, firstThemeSlug );
|
||||
}
|
||||
|
||||
for ( const key in themes ) {
|
||||
const slug = themes[ key ];
|
||||
|
||||
// Skip the first theme.
|
||||
if ( firstThemeSlug && firstThemeSlug === slug ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ensure that all the theme settings are present.
|
||||
theme = { ...allThemes.default, ...( allThemes[ slug ] || {} ) };
|
||||
theme.settings = { ...allThemes.default.settings, ...( theme.settings || {} ) };
|
||||
|
||||
itemsHtml += app.getThemesItemMarkup( theme, slug, selectedTheme );
|
||||
}
|
||||
|
||||
html = `<div role="radiogroup" class="wpforms-builder-themes-radio-group">
|
||||
${ itemsHtml }
|
||||
</div>`;
|
||||
|
||||
return html;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the theme item markup.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {Object} theme Theme data.
|
||||
* @param {string} slug Theme slug.
|
||||
* @param {string} selectedTheme Selected theme slug.
|
||||
*
|
||||
* @return {string} Theme item HTML.
|
||||
*/
|
||||
getThemesItemMarkup( theme, slug, selectedTheme ) {
|
||||
if ( ! theme ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const title = theme.name?.length > 0 ? theme.name : strings.theme_noname;
|
||||
let radioClasses = 'wpforms-builder-themes-radio ';
|
||||
const buttonClass = slug === selectedTheme ? 'is-active' : '';
|
||||
|
||||
radioClasses += app.isDisabledTheme( slug ) ? 'wpforms-builder-themes-radio-disabled' : ' wpforms-builder-themes-radio-enabled';
|
||||
|
||||
return `<button type="button" class="${ buttonClass }" value="${ slug }" role="radio">
|
||||
<div class="wpforms-builder-themes-radio ${ radioClasses }">
|
||||
<div class="wpforms-builder-themes-radio-title">${ title }</div>
|
||||
</div>
|
||||
|
||||
<div class="wpforms-builder-themes-indicators">
|
||||
<span class="component-color-indicator" title="${ strings.button_background }" style="background: ${ theme.settings.buttonBackgroundColor };" data-index="0"></span>
|
||||
<span class="component-color-indicator" title="${ strings.button_text }" style="background: ${ theme.settings.buttonTextColor }" data-index="1"></span>
|
||||
<span class="component-color-indicator" title="${ strings.field_label }" style="background: ${ theme.settings.labelColor };" data-index="2"></span>
|
||||
<span class="component-color-indicator" title="${ strings.field_sublabel } " style="background: ${ theme.settings.labelSublabelColor };" data-index="3"></span>
|
||||
<span class="component-color-indicator" title="${ strings.field_border }" style="background: ${ theme.settings.fieldBorderColor };" data-index="4"></span>
|
||||
</div>
|
||||
</button>`;
|
||||
},
|
||||
|
||||
/**
|
||||
* Show or hide the custom theme rename input.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
toggleCustomThemeSettings() {
|
||||
if ( ! isAdmin ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const value = WPFormsBuilderThemes.store.get( 'isCustomTheme' ) ?? '';
|
||||
const shouldShow = value === 'true';
|
||||
|
||||
el.$customThemeRenamer.toggleClass( 'wpforms-hidden', ! shouldShow );
|
||||
el.$customThemeRemover.toggleClass( 'wpforms-hidden', ! shouldShow );
|
||||
},
|
||||
|
||||
/**
|
||||
* On settings change event handler.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
addThemesEvents() {
|
||||
const $radioButtons = el.$themesControl.find( '[role="radio"]' );
|
||||
|
||||
// Add event listeners to the radio buttons.
|
||||
$radioButtons.off( 'click' ).on( 'click', function() {
|
||||
$radioButtons.removeClass( 'is-active' );
|
||||
|
||||
$( this ).addClass( 'is-active' );
|
||||
|
||||
const selectedValue = $( this ).val();
|
||||
|
||||
app.selectTheme( selectedValue );
|
||||
} );
|
||||
|
||||
// Add event listeners to the theme delete button.
|
||||
el.$customThemeRemover
|
||||
.off( 'click' )
|
||||
.on( 'click', app.deleteThemeModal );
|
||||
},
|
||||
|
||||
/**
|
||||
* Select theme event handler.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} value New attribute value.
|
||||
*/
|
||||
selectTheme( value ) {
|
||||
if ( ! app.setFormTheme( value ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
app.onSelectThemeWithBG( value );
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the form theme.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} themeSlug The theme slug.
|
||||
*
|
||||
* @return {boolean} True on success.
|
||||
*/
|
||||
setFormTheme( themeSlug ) {
|
||||
if ( app.maybeDisplayUpgradeModal( themeSlug ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const theme = app.getTheme( themeSlug );
|
||||
|
||||
if ( ! theme?.settings ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const attributes = Object.keys( theme.settings );
|
||||
const isCustomTheme = !! themesData.custom[ themeSlug ];
|
||||
|
||||
// Set the theme settings.
|
||||
WPFormsBuilderThemes.store.set( 'wpformsTheme', themeSlug );
|
||||
WPFormsBuilderThemes.store.set( 'isCustomTheme', isCustomTheme ? 'true' : '' );
|
||||
WPFormsBuilderThemes.store.set( 'themeName', isCustomTheme ? themesData.custom[ themeSlug ].name : '' );
|
||||
|
||||
// Clean up the settings.
|
||||
const cleanSettings = {};
|
||||
|
||||
for ( const key in attributes ) {
|
||||
const attr = attributes[ key ];
|
||||
const value = theme.settings[ attr ];
|
||||
|
||||
cleanSettings[ attr ] = typeof value === 'string'
|
||||
? value.replace( /px$/, '' )
|
||||
: value;
|
||||
}
|
||||
|
||||
// Update the theme settings.
|
||||
app.updateStylesAtts( cleanSettings );
|
||||
|
||||
//Reinit color pickers.
|
||||
WPFormsBuilderThemes.common.loadColorPickers();
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Open stock photos install modal on the select theme.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} themeSlug The theme slug.
|
||||
*/
|
||||
onSelectThemeWithBG( themeSlug ) {
|
||||
if ( WPFormsBuilderThemes.stockPhotos.isPicturesAvailable() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check only WPForms themes.
|
||||
if ( ! app.isWPFormsTheme( themeSlug ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {Object|null}
|
||||
* @property {Object|null} settings Settings.
|
||||
*/
|
||||
const theme = app.getTheme( themeSlug );
|
||||
const bgUrl = theme.settings?.backgroundUrl;
|
||||
|
||||
if ( bgUrl?.length && bgUrl !== 'url()' ) {
|
||||
WPFormsBuilderThemes.stockPhotos.installModal( 'themes' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Update styles atts.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {Object} themeSettings Theme settings.
|
||||
*/
|
||||
updateStylesAtts( themeSettings ) {
|
||||
const allowedKeys = WPFormsBuilderThemes.common.getStyleAttributesKeys();
|
||||
const validSettings = {};
|
||||
|
||||
for ( const key in themeSettings ) {
|
||||
if ( ! allowedKeys.includes( key ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let value = themeSettings[ key ];
|
||||
|
||||
if ( key === 'backgroundUrl' && typeof value === 'string' ) {
|
||||
value = app.getBackgroundUrl( value );
|
||||
}
|
||||
|
||||
validSettings[ key ] = value;
|
||||
}
|
||||
|
||||
// Update the settings.
|
||||
if ( Object.keys( validSettings ).length ) {
|
||||
Object.entries( validSettings ).forEach( ( [ key, value ] ) => {
|
||||
WPFormsBuilderThemes.store.set( key, value );
|
||||
} );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Extract the background URL from the string.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} value Background value.
|
||||
*
|
||||
* @return {string} Extracted background image url.
|
||||
*/
|
||||
getBackgroundUrl( value ) {
|
||||
const match = value.match( /^url\(\s*['"]?(.*?)['"]?\s*\)$/i );
|
||||
return match?.[ 1 ] || 'url()';
|
||||
},
|
||||
|
||||
/**
|
||||
* Get all themes data.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @return {Object} Themes data.
|
||||
*/
|
||||
getAllThemes() {
|
||||
return { ...( themesData.custom || {} ), ...( themesData.wpforms || {} ) };
|
||||
},
|
||||
|
||||
/**
|
||||
* Get theme data.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} slug Theme slug.
|
||||
*
|
||||
* @return {Object|null} Theme settings.
|
||||
*/
|
||||
getTheme( slug ) {
|
||||
return app.getAllThemes()[ slug ] || null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get enabled themes data.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @return {Object} Themes data.
|
||||
*/
|
||||
getEnabledThemes() {
|
||||
if ( enabledThemes ) {
|
||||
return enabledThemes;
|
||||
}
|
||||
|
||||
const allThemes = app.getAllThemes();
|
||||
|
||||
if ( isPro && isLicenseActive ) {
|
||||
return allThemes;
|
||||
}
|
||||
|
||||
enabledThemes = Object.keys( allThemes ).reduce( ( acc, key ) => {
|
||||
if ( allThemes[ key ].settings?.fieldSize && ! allThemes[ key ].disabled ) {
|
||||
acc[ key ] = allThemes[ key ];
|
||||
}
|
||||
return acc;
|
||||
}, {} );
|
||||
|
||||
return enabledThemes;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update enabled themes.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} slug Theme slug.
|
||||
* @param {Object} theme Theme settings.
|
||||
*/
|
||||
updateEnabledThemes( slug, theme ) {
|
||||
if ( ! enabledThemes ) {
|
||||
return;
|
||||
}
|
||||
|
||||
enabledThemes = {
|
||||
...enabledThemes,
|
||||
[ slug ]: theme,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether the theme is disabled.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} slug Theme slug.
|
||||
*
|
||||
* @return {boolean} True if the theme is disabled.
|
||||
*/
|
||||
isDisabledTheme( slug ) {
|
||||
return ! app.getEnabledThemes()?.[ slug ];
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether the theme is one of the WPForms themes.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} slug Theme slug.
|
||||
*
|
||||
* @return {boolean} True if the theme is one of the WPForms themes.
|
||||
*/
|
||||
isWPFormsTheme( slug ) {
|
||||
return Boolean( themesData.wpforms[ slug ]?.settings );
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch themes data from Rest API.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
fetchThemesData() {
|
||||
// If a fetch is already in progress, exit the function.
|
||||
if ( state.isFetchingThemes || themesData.wpforms ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the flag to true indicating a fetch is in progress.
|
||||
state.isFetchingThemes = true;
|
||||
|
||||
try {
|
||||
// Fetch themes data.
|
||||
wp.apiFetch( {
|
||||
path: routeNamespace + 'themes/',
|
||||
method: 'GET',
|
||||
cache: 'no-cache',
|
||||
} )
|
||||
.then( ( response ) => {
|
||||
themesData.wpforms = response.wpforms || {};
|
||||
themesData.custom = response.custom || {};
|
||||
|
||||
el.$window.trigger( 'wpformsBuilderThemesDataLoaded' );
|
||||
} )
|
||||
.catch( ( error ) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error( error?.message );
|
||||
} )
|
||||
.finally( () => {
|
||||
state.isFetchingThemes = false;
|
||||
} );
|
||||
} catch ( error ) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error( error );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Save custom themes.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
saveCustomThemes() {
|
||||
// Custom themes do not exist.
|
||||
if ( state.isSavingThemes || ! themesData.custom || ! isAdmin ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the flag to true indicating a saving is in progress.
|
||||
state.isSavingThemes = true;
|
||||
|
||||
try {
|
||||
// Save themes.
|
||||
wp.apiFetch( {
|
||||
path: routeNamespace + 'themes/custom/',
|
||||
method: 'POST',
|
||||
data: { customThemes: themesData.custom },
|
||||
} )
|
||||
.then( ( response ) => {
|
||||
if ( ! response?.result ) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log( response?.error );
|
||||
}
|
||||
} )
|
||||
.catch( ( error ) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error( error?.message );
|
||||
} )
|
||||
.finally( () => {
|
||||
state.isSavingThemes = false;
|
||||
} );
|
||||
} catch ( error ) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error( error );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the current style attributes state.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {Object} settings Settings.
|
||||
*
|
||||
* @return {Array} Current style attributes.
|
||||
*/
|
||||
getCurrentStyleAttributes( settings ) {
|
||||
const defaultAttributes = Object.keys( themesData.wpforms.default?.settings );
|
||||
const currentStyleAttributes = {};
|
||||
|
||||
for ( const key in defaultAttributes ) {
|
||||
const attr = defaultAttributes[ key ];
|
||||
|
||||
currentStyleAttributes[ attr ] = WPFormsBuilderThemes.common.prepareComplexAttrValues( settings[ attr ], attr ) ?? '';
|
||||
}
|
||||
|
||||
return currentStyleAttributes;
|
||||
},
|
||||
|
||||
/**
|
||||
* Maybe create a custom theme.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
*
|
||||
* @return {boolean} Whether the custom theme is created.
|
||||
*/
|
||||
maybeCreateCustomTheme() {
|
||||
const settings = WPFormsBuilderThemes.getSettings();
|
||||
const currentStyles = app.getCurrentStyleAttributes( settings );
|
||||
const isWPFormsTheme = !! themesData.wpforms[ settings.wpformsTheme ];
|
||||
const isCustomTheme = !! themesData.custom[ settings.wpformsTheme ];
|
||||
|
||||
// It is one of the default themes without any changes.
|
||||
if (
|
||||
isWPFormsTheme &&
|
||||
app.getPreparedDefaultThemeSettings( themesData.wpforms[ settings.wpformsTheme ]?.settings ) === JSON.stringify( currentStyles )
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// It is a modified default theme OR unknown custom theme.
|
||||
if ( isWPFormsTheme || ! isCustomTheme ) {
|
||||
app.createCustomTheme( settings, currentStyles );
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Prepare default theme settings for comparing.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {Object} settings Theme properties.
|
||||
*
|
||||
* @return {string} Whether the custom theme is created.
|
||||
*/
|
||||
getPreparedDefaultThemeSettings( settings ) {
|
||||
const preparedSettings = {};
|
||||
|
||||
Object.keys( settings ).forEach( ( key ) => {
|
||||
preparedSettings[ key ] = WPFormsBuilderThemes.common.removeRgbaSpaces( settings[ key ] );
|
||||
} );
|
||||
|
||||
return JSON.stringify( preparedSettings );
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a custom theme.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {Object} settings Style settings.
|
||||
* @param {Object} currentStyles Current style settings.
|
||||
*
|
||||
* @return {boolean} Whether the custom theme is created.
|
||||
*/
|
||||
createCustomTheme( settings, currentStyles = null ) {
|
||||
let counter = 0;
|
||||
let themeSlug = settings.wpformsTheme;
|
||||
|
||||
const baseTheme = app.getTheme( settings.wpformsTheme ) || themesData.wpforms.default;
|
||||
let themeName = baseTheme.name;
|
||||
|
||||
themesData.custom = themesData.custom || {};
|
||||
|
||||
// Determine the theme slug and the number of copies.
|
||||
do {
|
||||
counter++;
|
||||
themeSlug = themeSlug + '-copy-' + counter;
|
||||
} while ( themesData.custom[ themeSlug ] && counter < 10000 );
|
||||
|
||||
const copyStr = counter < 2 ? strings.theme_copy : strings.theme_copy + ' ' + counter;
|
||||
|
||||
themeName += ' (' + copyStr + ')';
|
||||
|
||||
// Add the new custom theme.
|
||||
themesData.custom[ themeSlug ] = {
|
||||
name: themeName,
|
||||
settings: currentStyles || app.getCurrentStyleAttributes( settings ),
|
||||
};
|
||||
|
||||
app.updateEnabledThemes( themeSlug, themesData.custom[ themeSlug ] );
|
||||
|
||||
// Update the settings with the new custom theme settings.
|
||||
WPFormsBuilderThemes.store.set( 'wpformsTheme', themeSlug );
|
||||
WPFormsBuilderThemes.store.set( 'isCustomTheme', 'true' );
|
||||
WPFormsBuilderThemes.store.set( 'themeName', themeName );
|
||||
|
||||
app.updateThemesList();
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update custom theme.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} attribute Attribute name.
|
||||
* @param {string} value New attribute value.
|
||||
*/
|
||||
updateCustomThemeAttribute( attribute, value ) {
|
||||
const settings = WPFormsBuilderThemes.getSettings();
|
||||
const themeSlug = settings.wpformsTheme;
|
||||
|
||||
// Skip if it is one of the WPForms themes OR the attribute is not in the theme settings.
|
||||
if (
|
||||
themesData.wpforms[ themeSlug ] ||
|
||||
(
|
||||
attribute !== 'themeName' &&
|
||||
! themesData.wpforms.default.settings[ attribute ]
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip if the custom theme doesn't exist in some rare cases.
|
||||
if ( ! themesData.custom[ themeSlug ] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the theme data.
|
||||
if ( attribute === 'themeName' ) {
|
||||
themesData.custom[ themeSlug ].name = value;
|
||||
} else {
|
||||
themesData.custom[ themeSlug ].settings = themesData.custom[ themeSlug ].settings || themesData.wpforms.default.settings;
|
||||
themesData.custom[ themeSlug ].settings[ attribute ] = value;
|
||||
|
||||
app.maybeUpdateColorIndicator( attribute, value );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Maybe update the custom theme settings.
|
||||
*
|
||||
* @param {string} key Setting key.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
maybeUpdateCustomTheme( key ) {
|
||||
const settings = WPFormsBuilderThemes.getSettings();
|
||||
const isCustomTheme = settings.isCustomTheme === 'true';
|
||||
|
||||
if ( ! isCustomTheme ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const attrValue = WPFormsBuilderThemes.common.prepareComplexAttrValues( settings[ key ], key );
|
||||
|
||||
app.updateCustomThemeAttribute( key, attrValue );
|
||||
},
|
||||
|
||||
/**
|
||||
* Maybe update the color indicators for the custom theme.
|
||||
*
|
||||
* @param {string} settingKey Setting key.
|
||||
* @param {string} settingValue Setting value.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
maybeUpdateColorIndicator( settingKey, settingValue ) {
|
||||
const colorSettingKeys = [ 'buttonBackgroundColor', 'buttonTextColor', 'labelColor', 'labelSublabelColor', 'fieldBorderColor' ];
|
||||
|
||||
if ( ! colorSettingKeys.includes( settingKey ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const $indicators = el.$themesControl.find( 'button.is-active .wpforms-builder-themes-indicators' );
|
||||
const indicatorIndex = colorSettingKeys.indexOf( settingKey );
|
||||
const $indicator = $indicators.find( `.component-color-indicator[data-index="${ indicatorIndex }"]` );
|
||||
|
||||
if ( $indicator.length ) {
|
||||
$indicator.css( 'background-color', settingValue );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Maybe display upgrades modal in Lite.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} themeSlug The theme slug.
|
||||
*
|
||||
* @return {boolean} True if modal was displayed.
|
||||
*/
|
||||
maybeDisplayUpgradeModal( themeSlug ) {
|
||||
if ( ! app.isDisabledTheme( themeSlug ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! isPro ) {
|
||||
WPFormsBuilderThemes.common.showProModal( 'themes', strings.pro_sections.themes );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( ! isLicenseActive ) {
|
||||
WPFormsBuilderThemes.common.showLicenseModal( 'themes', strings.pro_sections.themes, 'select-theme' );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Change theme name event handler.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {string} value New attribute value.
|
||||
*/
|
||||
changeThemeName( value ) {
|
||||
app.updateCustomThemeAttribute( 'themeName', value );
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete theme event handler.
|
||||
*
|
||||
* @param {string} deleteThemeSlug Theme slug.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
deleteTheme( deleteThemeSlug ) {
|
||||
// Remove theme from the theme storage.
|
||||
delete themesData.custom[ deleteThemeSlug ];
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the theme delete the confirmation modal window.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*
|
||||
* @param {Object} e Event object.
|
||||
*/
|
||||
deleteThemeModal( e ) {
|
||||
e.preventDefault();
|
||||
|
||||
const settings = WPFormsBuilderThemes.getSettings();
|
||||
const selectedThemeSlug = settings.wpformsTheme;
|
||||
const selectedThemeName = app.getTheme( selectedThemeSlug )?.name;
|
||||
const confirm = strings.theme_delete_confirm.replace( '%1$s', `<b>${ _.escape( selectedThemeName ) }</b>` );
|
||||
const content = `<p class="wpforms-theme-delete-text">${ confirm } ${ strings.theme_delete_cant_undone }</p>`;
|
||||
|
||||
$.confirm( {
|
||||
title: strings.theme_delete_title,
|
||||
content,
|
||||
icon: 'wpforms-exclamation-circle',
|
||||
type: 'red wpforms-builder-themes-modal',
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: strings.theme_delete_yes,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
action() {
|
||||
// Delete the theme and switch to the default theme.
|
||||
app.deleteTheme( selectedThemeSlug );
|
||||
app.selectTheme( 'default' );
|
||||
},
|
||||
},
|
||||
cancel: {
|
||||
text: strings.cancel,
|
||||
keys: [ 'esc' ],
|
||||
},
|
||||
},
|
||||
} );
|
||||
},
|
||||
};
|
||||
|
||||
return app;
|
||||
}
|
||||
Vendored
Executable
+15
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user