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:
+70
@@ -0,0 +1,70 @@
|
||||
/* global $e, elementor */
|
||||
|
||||
// noinspection ES6ConvertVarToLetConst
|
||||
/**
|
||||
* WPForms script for editor context.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*/
|
||||
var WPFormsElementorEditorContext = window.WPFormsElementorEditorContext || ( function( document, window, $ ) { // eslint-disable-line no-var
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const app = {
|
||||
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*/
|
||||
init() {
|
||||
app.events();
|
||||
},
|
||||
|
||||
/**
|
||||
* Register JS events.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*/
|
||||
events() {
|
||||
$( window ).on( 'elementor/init', function() {
|
||||
// To add action on save event, we should use hookUI.After.
|
||||
$e.hooks.registerUIAfter( new class extends $e.modules.hookUI.After {
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
getCommand() {
|
||||
return 'document/save/save';
|
||||
}
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
getId() {
|
||||
return 'wpforms-elementor-editor-context-after-save';
|
||||
}
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
getConditions() {
|
||||
return true;
|
||||
}
|
||||
|
||||
apply() {
|
||||
// Save custom themes in a preview window.
|
||||
const previewWindow = elementor.$preview[ 0 ]?.contentWindow;
|
||||
|
||||
if ( previewWindow ) {
|
||||
previewWindow.WPFormsElementorThemes.saveCustomThemes();
|
||||
}
|
||||
}
|
||||
} );
|
||||
} );
|
||||
},
|
||||
};
|
||||
|
||||
return app;
|
||||
}( document, window, jQuery ) );
|
||||
|
||||
// Initialize.
|
||||
WPFormsElementorEditorContext.init();
|
||||
Vendored
Executable
+1
@@ -0,0 +1 @@
|
||||
var WPFormsElementorEditorContext=window.WPFormsElementorEditorContext||((e,t)=>{let o={init(){o.events()},events(){t(e).on("elementor/init",function(){$e.hooks.registerUIAfter(new class extends $e.modules.hookUI.After{getCommand(){return"document/save/save"}getId(){return"wpforms-elementor-editor-context-after-save"}getConditions(){return!0}apply(){var e=elementor.$preview[0]?.contentWindow;e&&e.WPFormsElementorThemes.saveCustomThemes()}})})}};return o})((document,window),jQuery);WPFormsElementorEditorContext.init();
|
||||
+1256
File diff suppressed because it is too large
Load Diff
Vendored
Executable
+4
File diff suppressed because one or more lines are too long
+623
@@ -0,0 +1,623 @@
|
||||
/* global wpformsElementorVars, elementor, elementorFrontend */
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* WPForms integration with Elementor in the editor.
|
||||
*
|
||||
* @since 1.6.0
|
||||
* @since 1.6.2 Moved frontend integration to `wpforms-elementor-frontend.js`
|
||||
*/
|
||||
var WPFormsElementor = window.WPFormsElementor || ( function( document, window, $ ) {
|
||||
|
||||
/**
|
||||
* Runtime variables.
|
||||
*
|
||||
* @since 1.6.2
|
||||
*
|
||||
* @type {object}
|
||||
*/
|
||||
var vars = {};
|
||||
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.6.0
|
||||
*
|
||||
* @type {object}
|
||||
*/
|
||||
var app = {
|
||||
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.6.0
|
||||
*/
|
||||
init: function() {
|
||||
|
||||
app.events();
|
||||
},
|
||||
|
||||
/**
|
||||
* Register JS events.
|
||||
*
|
||||
* @since 1.6.0
|
||||
*/
|
||||
events: function() {
|
||||
|
||||
// Widget events.
|
||||
$( window ).on( 'elementor/frontend/init', function( event, id, instance ) {
|
||||
|
||||
// Widget buttons click.
|
||||
elementor.channels.editor.on( 'elementorWPFormsAddFormBtnClick', app.addFormBtnClick );
|
||||
|
||||
// Widget frontend events.
|
||||
elementorFrontend.hooks.addAction( 'frontend/element_ready/wpforms.default', app.widgetPreviewEvents );
|
||||
|
||||
// Initialize widget controls.
|
||||
elementor.hooks.addAction( 'panel/open_editor/widget/wpforms', app.widgetPanelOpen );
|
||||
|
||||
// Initialize choiceJS.
|
||||
elementorFrontend.hooks.addAction( 'frontend/element_ready/wpforms.default', app.loadChoicesJS );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Init Modern style Dropdown fields (<select>) with choiceJS.
|
||||
*
|
||||
* @since 1.9.0
|
||||
*
|
||||
* @param {Object} $scope Elementor scope object.
|
||||
*/
|
||||
loadChoicesJS( $scope ) {
|
||||
// Loads if function exists.
|
||||
if ( typeof parent.Choices !== 'function' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const $elements = $scope.find( '.wpforms-field .choicesjs-select' );
|
||||
const config = window.wpforms_choicesjs_config || {};
|
||||
|
||||
// Initialize ChoicesJS.
|
||||
$elements.each( function( index, el ) {
|
||||
if ( ! ( el instanceof parent.HTMLSelectElement ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const $el = $( el );
|
||||
|
||||
if ( $el.data( 'choicesjs' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const $field = $el.closest( '.wpforms-field' );
|
||||
|
||||
config.callbackOnInit = function() {
|
||||
const self = this,
|
||||
$element = $( self.passedElement.element ),
|
||||
$input = $( self.input.element ),
|
||||
sizeClass = $element.data( 'size-class' );
|
||||
|
||||
// Add CSS-class for size.
|
||||
if ( sizeClass ) {
|
||||
$( self.containerOuter.element ).addClass( sizeClass );
|
||||
}
|
||||
|
||||
/**
|
||||
* If a multiple select has selected choices - hide a placeholder text.
|
||||
* In case if select is empty - we return placeholder text.
|
||||
*/
|
||||
if ( $element.prop( 'multiple' ) ) {
|
||||
// On init event.
|
||||
$input.data( 'placeholder', $input.attr( 'placeholder' ) );
|
||||
|
||||
if ( self.getValue( true ).length ) {
|
||||
$input.hide();
|
||||
}
|
||||
}
|
||||
|
||||
this.disable();
|
||||
$field.find( '.is-disabled' ).removeClass( 'is-disabled' );
|
||||
};
|
||||
$el.data( 'choicesjs', new parent.Choices( el, config ) );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Widget events.
|
||||
*
|
||||
* @since 1.6.2
|
||||
*
|
||||
* @param {jQuery} $scope The current element wrapped with jQuery.
|
||||
*/
|
||||
widgetPreviewEvents: function( $scope ) {
|
||||
|
||||
$scope
|
||||
.on( 'click', '.wpforms-btn', app.addFormBtnClick )
|
||||
.on( 'click', '.wpforms-admin-no-forms-container a', app.clickLinkInPreview )
|
||||
.on( 'change', '.wpforms-elementor-form-selector select', app.selectFormInPreview )
|
||||
.on( 'click mousedown focus keydown submit', '.wpforms-container *', app.disableEvents )
|
||||
.on( 'click', '.wpforms-comprehensive-link', app.openComprehensiveLink );
|
||||
|
||||
app.updateSameForms( $scope );
|
||||
},
|
||||
|
||||
/**
|
||||
* Update all the same forms on the preview.
|
||||
*
|
||||
* @since 1.6.3
|
||||
*
|
||||
* @param {jQuery} $scope The current element wrapped with jQuery.
|
||||
*/
|
||||
updateSameForms: function( $scope ) {
|
||||
|
||||
var elementId = $scope.data( 'id' ),
|
||||
$formContainer = $scope.find( '.wpforms-container' ),
|
||||
formContainerHtml = $formContainer.html(),
|
||||
formContainerId = $formContainer.attr( 'id' );
|
||||
|
||||
$scope
|
||||
.closest( '.elementor-editor-active' )
|
||||
.find( '.elementor-widget-wpforms:not(.elementor-element-' + elementId + ')' )
|
||||
.each( function() {
|
||||
|
||||
var $anotherFormContainer = $( this ).find( '.wpforms-container' );
|
||||
|
||||
if ( $anotherFormContainer.attr( 'id' ) === formContainerId ) {
|
||||
$anotherFormContainer.html( formContainerHtml );
|
||||
}
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize widget controls when widget is activated.
|
||||
*
|
||||
* @since 1.6.2
|
||||
*
|
||||
* @param {object} panel Panel object.
|
||||
* @param {object} model Model object.
|
||||
*/
|
||||
widgetPanelOpen: function( panel, model ) {
|
||||
|
||||
vars.widgetId = model.attributes.id;
|
||||
vars.formId = model.attributes.settings.attributes.form_id;
|
||||
|
||||
app.widgetPanelInit( panel );
|
||||
|
||||
app.widgetPanelObserver.init( panel );
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize widget controls when widget is activated.
|
||||
*
|
||||
* @since 1.6.2
|
||||
*
|
||||
* @param {object} panel Panel object.
|
||||
*/
|
||||
widgetPanelInit: function( panel ) {
|
||||
|
||||
var $formSelectControl = panel.$el.find( '.elementor-control.elementor-control-form_id' ),
|
||||
$formSelect = $formSelectControl.find( 'select' ),
|
||||
$addFormNoticeControl = panel.$el.find( '.elementor-control.elementor-control-add_form_notice' ),
|
||||
$testFormNoticeControl = panel.$el.find( '.elementor-control.elementor-control-test_form_notice' );
|
||||
|
||||
// Update form select options if it is available after adding the form.
|
||||
if ( vars.formSelectOptions ) {
|
||||
$formSelect.html( vars.formSelectOptions );
|
||||
}
|
||||
|
||||
// Update form select value.
|
||||
if ( vars.formId && vars.formId !== '' ) {
|
||||
$formSelect.val( vars.formId );
|
||||
}
|
||||
|
||||
// Hide not needed controls.
|
||||
if ( $formSelect.find( 'option' ).length > 0 ) {
|
||||
$addFormNoticeControl.hide();
|
||||
} else {
|
||||
$formSelectControl.hide();
|
||||
$testFormNoticeControl.hide();
|
||||
}
|
||||
|
||||
// Show needed controls.
|
||||
if ( parseInt( $formSelect.val(), 10 ) > 0 ) {
|
||||
$testFormNoticeControl.show();
|
||||
}
|
||||
|
||||
// Select form.
|
||||
panel.$el.find( '.elementor-control.elementor-control-form_id' ).on( 'change', 'select', function() {
|
||||
|
||||
// Update `vars.formId` to be able to restore selected value after options update.
|
||||
vars.formId = $( this ).val();
|
||||
} );
|
||||
|
||||
// Click on the `Edit the selected form` link.
|
||||
panel.$el.find( '.elementor-control.elementor-control-edit_form' ).on( 'click', 'a', app.editFormLinkClick );
|
||||
},
|
||||
|
||||
/**
|
||||
* The observer needed to re-init controls when the widget panel section and tabs switches.
|
||||
*
|
||||
* @since 1.6.3
|
||||
*
|
||||
* @member {object}
|
||||
*/
|
||||
widgetPanelObserver: {
|
||||
|
||||
/**
|
||||
* Initialize observer.
|
||||
*
|
||||
* @since 1.6.3
|
||||
*
|
||||
* @param {object} panel Panel object.
|
||||
*/
|
||||
init: function( panel ) {
|
||||
|
||||
// Skip if observer for current widget already initialized.
|
||||
if ( vars.observerWidgetId === vars.widgetId ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Disconnect previous widget observer.
|
||||
if ( typeof vars.observer !== 'undefined' && typeof vars.observer.disconnect === 'function' ) {
|
||||
vars.observer.disconnect();
|
||||
}
|
||||
|
||||
var obs = {
|
||||
targetNode : panel.$el.find( '#elementor-panel-content-wrapper' )[0],
|
||||
config : {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: true,
|
||||
},
|
||||
};
|
||||
|
||||
app.widgetPanelObserver.panel = panel;
|
||||
|
||||
obs.observer = new MutationObserver( app.widgetPanelObserver.callback );
|
||||
obs.observer.observe( obs.targetNode, obs.config );
|
||||
|
||||
vars.observerWidgetId = vars.widgetId;
|
||||
vars.observer = obs.observer;
|
||||
},
|
||||
|
||||
/**
|
||||
* Observer callback.
|
||||
*
|
||||
* @since 1.6.3
|
||||
*
|
||||
* @param {Array} mutationsList Mutation list.
|
||||
*/
|
||||
callback: function( mutationsList ) {
|
||||
|
||||
var mutation,
|
||||
quit = false;
|
||||
|
||||
for ( var i in mutationsList ) {
|
||||
mutation = mutationsList[ i ];
|
||||
|
||||
if ( mutation.type === 'childList' && mutation.addedNodes.length > 0 ) {
|
||||
quit = app.widgetPanelObserver.callbackMutationChildList( mutation );
|
||||
}
|
||||
|
||||
if ( mutation.type === 'attributes' ) {
|
||||
quit = app.widgetPanelObserver.callbackMutationAttributes( mutation );
|
||||
}
|
||||
|
||||
if ( quit ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Process 'childList' mutation.
|
||||
*
|
||||
* @since 1.6.3
|
||||
*
|
||||
* @param {MutationRecord} mutation Mutation record.
|
||||
*
|
||||
* @returns {boolean} True if detect needed node.
|
||||
*/
|
||||
callbackMutationChildList: function( mutation ) {
|
||||
|
||||
var addedNodes = mutation.addedNodes || [],
|
||||
node;
|
||||
|
||||
for ( var n in addedNodes ) {
|
||||
node = addedNodes[ n ];
|
||||
|
||||
if ( node && node.classList && node.classList.contains( 'elementor-control-section_form' ) ) {
|
||||
app.widgetPanelInit( app.widgetPanelObserver.panel );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Process 'attributes' mutation.
|
||||
*
|
||||
* @since 1.6.3
|
||||
*
|
||||
* @param {MutationRecord} mutation Mutation record.
|
||||
*
|
||||
* @returns {boolean} True if detect needed target.
|
||||
*/
|
||||
callbackMutationAttributes: function( mutation ) {
|
||||
|
||||
if (
|
||||
mutation.target &&
|
||||
mutation.target.classList &&
|
||||
mutation.target.classList.contains( 'elementor-tab-control-content' )
|
||||
) {
|
||||
app.widgetPanelInit( app.widgetPanelObserver.panel );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Edit selected form button click event handler.
|
||||
*
|
||||
* @since 1.6.2
|
||||
*
|
||||
* @param {object} event Event object.
|
||||
*/
|
||||
editFormLinkClick: function( event ) {
|
||||
|
||||
app.findFormSelector( event );
|
||||
app.openBuilderPopup( vars.$select.val() );
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a new form button click event handler.
|
||||
*
|
||||
* @since 1.6.2
|
||||
*
|
||||
* @param {object} event Event object.
|
||||
*/
|
||||
addFormBtnClick: function( event ) {
|
||||
|
||||
app.findFormSelector( event );
|
||||
app.openBuilderPopup( 0 );
|
||||
},
|
||||
|
||||
/**
|
||||
* Find and store the form selector control wrapped in jQuery object.
|
||||
*
|
||||
* @since 1.6.2
|
||||
*
|
||||
* @param {object} event Event object.
|
||||
*/
|
||||
findFormSelector: function( event ) {
|
||||
|
||||
let view = elementor.getPanelView().getCurrentPageView();
|
||||
|
||||
// We need to be sure that we are on the widget Content section.
|
||||
if ( view.activeSection && view.activeSection !== 'section_form' ) {
|
||||
$( view.ui.tabs[0] ).trigger( 'click' );
|
||||
}
|
||||
|
||||
vars.$select = event && event.$el ?
|
||||
event.$el.closest( '#elementor-controls' ).find( 'select[data-setting="form_id"]' ) :
|
||||
window.parent.jQuery( '#elementor-controls select[data-setting="form_id"]' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Preview: Form selector event handler.
|
||||
*
|
||||
* @since 1.6.2
|
||||
*/
|
||||
selectFormInPreview: function() {
|
||||
|
||||
vars.formId = $( this ).val();
|
||||
|
||||
app.findFormSelector();
|
||||
|
||||
// To be sure, that both form selector selects are in sync.
|
||||
app.refreshFormsList( null, vars.formId );
|
||||
},
|
||||
|
||||
/**
|
||||
* Preview: Click on the link event handler.
|
||||
*
|
||||
* @since 1.6.2
|
||||
*
|
||||
* @param {object} event Event object.
|
||||
*/
|
||||
clickLinkInPreview: function( event ) {
|
||||
|
||||
if ( event.target && event.target.href ) {
|
||||
window.open( event.target.href, '_blank', 'noopener,noreferrer' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Disable events.
|
||||
*
|
||||
* @since 1.6.2
|
||||
*
|
||||
* @param {object} event Event object.
|
||||
*
|
||||
* @returns {boolean} Always false.
|
||||
*/
|
||||
disableEvents: function( event ) {
|
||||
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the compreshenvie guide link,
|
||||
* as elementor disables all links in the preview.
|
||||
*
|
||||
* @since 1.8.3
|
||||
*
|
||||
* @param {object} event Event object.
|
||||
*/
|
||||
openComprehensiveLink: function( event ) {
|
||||
|
||||
const url = $( this ).attr( 'href' );
|
||||
|
||||
// Open the url in a new tab with JS bc elementor doesn't allow links in the preview.
|
||||
window.open( url, '_blank' ).focus();
|
||||
},
|
||||
|
||||
/**
|
||||
* Open builder popup.
|
||||
*
|
||||
* @since 1.6.2
|
||||
*
|
||||
* @param {number} formId Form id. 0 for create new form.
|
||||
*/
|
||||
openBuilderPopup: function( formId ) {
|
||||
|
||||
formId = parseInt( formId || '0', 10 );
|
||||
|
||||
if ( ! vars.$popup ) {
|
||||
|
||||
// We need to add popup markup to the editor top document.
|
||||
var $elementor = window.parent.jQuery( '#elementor-editor-wrapper' ),
|
||||
popupTpl = wp.template( 'wpforms-builder-elementor-popup' );
|
||||
|
||||
$elementor.after( popupTpl() );
|
||||
vars.$popup = $elementor.siblings( '#wpforms-builder-elementor-popup' );
|
||||
}
|
||||
|
||||
var url = formId > 0 ? wpformsElementorVars.edit_form_url + formId : wpformsElementorVars.add_form_url,
|
||||
$iframe = vars.$popup.find( 'iframe' );
|
||||
|
||||
app.builderCloseButtonEvent();
|
||||
$iframe.attr( 'src', url );
|
||||
vars.$popup.fadeIn();
|
||||
},
|
||||
|
||||
/**
|
||||
* Close button (inside the form builder) click event.
|
||||
*
|
||||
* @since 1.6.2
|
||||
*/
|
||||
builderCloseButtonEvent: function() {
|
||||
|
||||
vars.$popup
|
||||
.off( 'wpformsBuilderInPopupClose' )
|
||||
.on( 'wpformsBuilderInPopupClose', function( e, action, formId ) {
|
||||
|
||||
if ( action !== 'saved' || ! formId ) {
|
||||
return;
|
||||
}
|
||||
|
||||
app.refreshFormsList( null, formId );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Refresh forms list event handler.
|
||||
*
|
||||
* @since 1.6.2
|
||||
*
|
||||
* @param {object} event Event object.
|
||||
* @param {number} setFormId Set selected form to.
|
||||
*/
|
||||
refreshFormsList: function( event, setFormId ) {
|
||||
|
||||
if ( event ) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
app.findFormSelector();
|
||||
|
||||
var data = {
|
||||
action: 'wpforms_admin_get_form_selector_options',
|
||||
nonce : wpformsElementorVars.nonce,
|
||||
};
|
||||
|
||||
vars.$select.prop( 'disabled', true );
|
||||
|
||||
$.post( wpformsElementorVars.ajax_url, data )
|
||||
.done( function( response ) {
|
||||
|
||||
if ( ! response.success ) {
|
||||
app.debug( response );
|
||||
return;
|
||||
}
|
||||
|
||||
vars.formSelectOptions = response.data;
|
||||
vars.$select.html( response.data );
|
||||
|
||||
if ( setFormId ) {
|
||||
vars.formId = setFormId;
|
||||
}
|
||||
|
||||
if ( vars.formId && vars.formId !== '' ) {
|
||||
vars.$select.val( vars.formId ).trigger( 'change' );
|
||||
}
|
||||
} )
|
||||
.fail( function( xhr, textStatus ) {
|
||||
|
||||
app.debug( {
|
||||
xhr: xhr,
|
||||
textStatus: textStatus,
|
||||
} );
|
||||
} )
|
||||
.always( function() {
|
||||
|
||||
if ( ! vars.$select || vars.$select.length < 1 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
vars.$select.prop( 'disabled', false );
|
||||
|
||||
var $formSelectOptions = vars.$select.find( 'option' ),
|
||||
$formSelectControl = vars.$select.closest( '.elementor-control' );
|
||||
|
||||
if ( $formSelectOptions.length > 0 ) {
|
||||
$formSelectControl.show();
|
||||
$formSelectControl.siblings( '.elementor-control-add_form_notice' ).hide();
|
||||
}
|
||||
if ( parseInt( vars.$select.val(), 10 ) > 0 ) {
|
||||
$formSelectControl.siblings( '.elementor-control-test_form_notice' ).show();
|
||||
}
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Debug output helper.
|
||||
*
|
||||
* @since 1.6.2
|
||||
*
|
||||
* @param {mixed} msg Debug message.
|
||||
*/
|
||||
debug: function( msg ) {
|
||||
|
||||
if ( app.isDebug() ) {
|
||||
console.log( 'WPForms Debug:', msg );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Is debug mode.
|
||||
*
|
||||
* @since 1.6.2
|
||||
*
|
||||
* @returns {boolean} True if the debug enabled.
|
||||
*/
|
||||
isDebug: function() {
|
||||
|
||||
return ( ( window.top.location.hash && '#wpformsdebug' === window.top.location.hash ) || wpformsElementorVars.debug );
|
||||
},
|
||||
};
|
||||
|
||||
return app;
|
||||
|
||||
}( document, window, jQuery ) );
|
||||
|
||||
// Initialize.
|
||||
WPFormsElementor.init();
|
||||
+1
File diff suppressed because one or more lines are too long
+154
@@ -0,0 +1,154 @@
|
||||
/* global wpforms, wpformsElementorVars, wpformsModernFileUpload, wpformsRecaptchaLoad, grecaptcha, WPFormsRepeaterField, WPFormsStripePaymentElement */
|
||||
|
||||
/**
|
||||
* WPForms integration with Elementor on the frontend.
|
||||
*
|
||||
* @since 1.6.2 Moved from `wpforms-elementor.js`
|
||||
*/
|
||||
var WPFormsElementorFrontend = window.WPFormsElementorFrontend || ( function( document, window, $ ) {
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.6.2
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
var app = {
|
||||
|
||||
/**
|
||||
* Flag to force load ChoicesJS.
|
||||
*
|
||||
* @since 1.9.0
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
forceLoadChoices: false,
|
||||
|
||||
/**
|
||||
* Flag to force set Stripe.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
forceSetStripe: false,
|
||||
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.6.2
|
||||
*/
|
||||
init() {
|
||||
app.events();
|
||||
},
|
||||
|
||||
/**
|
||||
* Register JS events.
|
||||
*
|
||||
* @since 1.6.2
|
||||
*/
|
||||
events() {
|
||||
window.addEventListener( 'elementor/popup/show', function( event ) {
|
||||
const $modal = $( '#elementor-popup-modal-' + event.detail.id ),
|
||||
$form = $modal.find( '.wpforms-form' );
|
||||
|
||||
if ( ! $form.length ) {
|
||||
return;
|
||||
}
|
||||
|
||||
app.forceSetStripe = true;
|
||||
|
||||
app.initFields( $form );
|
||||
} );
|
||||
|
||||
// Add Elementor popup support for text limit.
|
||||
window.addEventListener( 'elementor/popup/show', function() {
|
||||
window.WPFormsTextLimit?.initHint( '.elementor-popup-modal' );
|
||||
} );
|
||||
|
||||
// Force load ChoicesJS for elementor popup.
|
||||
$( document ).on( 'elementor/popup/show', () => {
|
||||
app.forceLoadChoices = true;
|
||||
|
||||
wpforms.loadChoicesJS();
|
||||
} );
|
||||
|
||||
$( document ).on( 'wpformsBeforeLoadElementChoices', ( event, el ) => {
|
||||
// Do not initialize on elementor popup.
|
||||
if ( ! app.isFormInElementorPopup( el ) || app.forceLoadChoices ) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
} );
|
||||
|
||||
$( document ).on( 'wpformsBeforeStripePaymentElementSetup', ( event, el ) => {
|
||||
// Do not initialize on elementor popup.
|
||||
if ( ! app.isFormInElementorPopup( el ) || app.forceSetStripe ) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if the form is in Elementor popup.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @param {Object} form Form element.
|
||||
*
|
||||
* @return {boolean} True if the form is in Elementor popup, false otherwise.
|
||||
*/
|
||||
isFormInElementorPopup( form ) {
|
||||
return $( form ).parents( 'div[data-elementor-type="popup"]' ).length;
|
||||
},
|
||||
|
||||
/**
|
||||
* Init all things for WPForms.
|
||||
*
|
||||
* @since 1.6.2
|
||||
*
|
||||
* @param {Object} $form jQuery selector.
|
||||
*/
|
||||
initFields( $form ) { // eslint-disable-line complexity
|
||||
// Init WPForms things.
|
||||
wpforms.ready();
|
||||
|
||||
// Init `Modern File Upload` field.
|
||||
if ( 'undefined' !== typeof wpformsModernFileUpload ) {
|
||||
wpformsModernFileUpload.init();
|
||||
}
|
||||
|
||||
// Init CAPTCHA.
|
||||
if ( 'undefined' !== typeof wpformsRecaptchaLoad ) {
|
||||
if ( 'recaptcha' === wpformsElementorVars.captcha_provider && 'v3' === wpformsElementorVars.recaptcha_type ) {
|
||||
if ( 'undefined' !== typeof grecaptcha ) {
|
||||
grecaptcha.ready( wpformsRecaptchaLoad );
|
||||
}
|
||||
} else {
|
||||
wpformsRecaptchaLoad();
|
||||
}
|
||||
}
|
||||
|
||||
// Init Repeater fields.
|
||||
if ( 'undefined' !== typeof WPFormsRepeaterField ) {
|
||||
WPFormsRepeaterField.ready();
|
||||
}
|
||||
|
||||
// Init Stripe payment.
|
||||
if ( 'undefined' !== typeof WPFormsStripePaymentElement ) {
|
||||
WPFormsStripePaymentElement.setupStripeForm( $form );
|
||||
}
|
||||
|
||||
// Register a custom event.
|
||||
$( document ).trigger( 'wpforms_elementor_form_fields_initialized', [ $form ] );
|
||||
},
|
||||
};
|
||||
|
||||
return app;
|
||||
}( document, window, jQuery ) );
|
||||
|
||||
// Initialize.
|
||||
WPFormsElementorFrontend.init();
|
||||
Vendored
Executable
+1
@@ -0,0 +1 @@
|
||||
var WPFormsElementorFrontend=window.WPFormsElementorFrontend||((o,e,r)=>{var t={forceLoadChoices:!1,forceSetStripe:!1,init(){t.events()},events(){e.addEventListener("elementor/popup/show",function(e){e=r("#elementor-popup-modal-"+e.detail.id).find(".wpforms-form");e.length&&(t.forceSetStripe=!0,t.initFields(e))}),e.addEventListener("elementor/popup/show",function(){e.WPFormsTextLimit?.initHint(".elementor-popup-modal")}),r(o).on("elementor/popup/show",()=>{t.forceLoadChoices=!0,wpforms.loadChoicesJS()}),r(o).on("wpformsBeforeLoadElementChoices",(e,o)=>{t.isFormInElementorPopup(o)&&!t.forceLoadChoices&&e.preventDefault()}),r(o).on("wpformsBeforeStripePaymentElementSetup",(e,o)=>{t.isFormInElementorPopup(o)&&!t.forceSetStripe&&e.preventDefault()})},isFormInElementorPopup(e){return r(e).parents('div[data-elementor-type="popup"]').length},initFields(e){wpforms.ready(),"undefined"!=typeof wpformsModernFileUpload&&wpformsModernFileUpload.init(),"undefined"!=typeof wpformsRecaptchaLoad&&("recaptcha"===wpformsElementorVars.captcha_provider&&"v3"===wpformsElementorVars.recaptcha_type?"undefined"!=typeof grecaptcha&&grecaptcha.ready(wpformsRecaptchaLoad):wpformsRecaptchaLoad()),"undefined"!=typeof WPFormsRepeaterField&&WPFormsRepeaterField.ready(),"undefined"!=typeof WPFormsStripePaymentElement&&WPFormsStripePaymentElement.setupStripeForm(e),r(o).trigger("wpforms_elementor_form_fields_initialized",[e])}};return t})(document,window,jQuery);WPFormsElementorFrontend.init();
|
||||
+965
@@ -0,0 +1,965 @@
|
||||
/* global elementor, elementorCommon, wpformsElementorVars, WPFormsElementorModern */
|
||||
// noinspection TypeScriptUMDGlobal
|
||||
|
||||
/**
|
||||
* @param wpformsElementorVars.route_namespace
|
||||
* @param strings.form_themes
|
||||
* @param strings.theme_name
|
||||
* @param strings.theme_delete
|
||||
* @param strings.theme_delete_title
|
||||
* @param strings.theme_delete_confirm
|
||||
* @param strings.theme_delete_cant_undone
|
||||
* @param strings.theme_delete_yes
|
||||
* @param strings.theme_copy
|
||||
* @param strings.theme_custom
|
||||
* @param strings.theme_noname
|
||||
* @param strings.themes_error
|
||||
* @param strings.button_background
|
||||
* @param strings.button_text
|
||||
* @param strings.field_label
|
||||
* @param strings.field_sublabel
|
||||
* @param strings.field_border
|
||||
*/
|
||||
|
||||
// noinspection ES6ConvertVarToLetConst
|
||||
/**
|
||||
* WPForms integration with Elementor (modern widget).
|
||||
*
|
||||
* @since 1.9.6
|
||||
*/
|
||||
var WPFormsElementorThemes = window.WPFormsElementorThemes || ( function( document, window, $ ) { // eslint-disable-line no-var
|
||||
/**
|
||||
* Localized data aliases.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*/
|
||||
const { isAdmin, isPro, isLicenseActive, strings, route_namespace: routeNamespace } = wpformsElementorVars;
|
||||
|
||||
/**
|
||||
* Runtime state.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const state = {};
|
||||
|
||||
/**
|
||||
* Themes data.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const themesData = {
|
||||
wpforms: null,
|
||||
custom: null,
|
||||
};
|
||||
|
||||
/**
|
||||
* Enabled themes.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
let enabledThemes = null;
|
||||
|
||||
/**
|
||||
* Elements holder.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const el = {};
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const app = {
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*/
|
||||
init() {
|
||||
el.$window = $( window );
|
||||
|
||||
app.fetchThemesData();
|
||||
|
||||
app.events();
|
||||
},
|
||||
|
||||
/**
|
||||
* Register JS events.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*/
|
||||
events() {
|
||||
// noinspection JSUnusedLocalSymbols
|
||||
$( window )
|
||||
.on( 'elementor/frontend/init', function() {
|
||||
elementor.channels.editor.on( 'section:activated', app.themesControlSetup );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Get all themes data.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*
|
||||
* @return {Object} Themes data.
|
||||
*/
|
||||
getAllThemes() {
|
||||
return { ...( themesData.custom || {} ), ...( themesData.wpforms || {} ) };
|
||||
},
|
||||
|
||||
/**
|
||||
* On section change event handler.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*
|
||||
* @param {string} sectionName The current section name.
|
||||
* @param {Object} editor Editor instance.
|
||||
*/
|
||||
themesControlSetup( sectionName, editor ) {
|
||||
if ( sectionName !== 'themes' || editor.model.attributes.widgetType !== 'wpforms' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const $panelContent = editor.$childViewContainer[ 0 ];
|
||||
const $themesControl = $( $panelContent ).find( '.wpforms-elementor-themes-control' );
|
||||
|
||||
// Scrollbar fix for Mac.
|
||||
if ( app.isMac() ) {
|
||||
$themesControl.addClass( 'wpforms-is-mac' );
|
||||
}
|
||||
|
||||
app.updateThemesList( editor, $themesControl );
|
||||
},
|
||||
|
||||
/**
|
||||
* Update themes list.
|
||||
*
|
||||
* @since 1.9.6
|
||||
* @param {Object} editor Editor instance.
|
||||
* @param {Object} $themesControl Themes control object.
|
||||
*/
|
||||
updateThemesList( editor, $themesControl ) {
|
||||
const selectedTheme = editor.model.attributes.settings.attributes.wpformsTheme ?? 'default';
|
||||
|
||||
// Get all themes.
|
||||
const html = app.getThemesListMarkup( selectedTheme );
|
||||
|
||||
$themesControl.html( html );
|
||||
|
||||
app.addThemesEvents( $themesControl, editor );
|
||||
},
|
||||
|
||||
/**
|
||||
* On settings change event handler.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*
|
||||
* @param {Object} $themesControl Themes control element.
|
||||
* @param {Object} editor Editor instance.
|
||||
*/
|
||||
addThemesEvents( $themesControl, editor ) {
|
||||
const debouncedMaybeCreate = _.debounce( ( settings ) => {
|
||||
app.maybeCreateCustomTheme( settings );
|
||||
}, 300 );
|
||||
|
||||
const settingsModel = editor.model.get( 'settings' );
|
||||
|
||||
if ( settingsModel.attributes.isMigrated !== 'true' ) {
|
||||
app.maybeMigrateToCustomTheme( settingsModel, $themesControl, editor );
|
||||
}
|
||||
|
||||
settingsModel.on( 'change', ( one ) => {
|
||||
debouncedMaybeCreate( one.attributes );
|
||||
app.maybeUpdateCustomTheme( one );
|
||||
} );
|
||||
|
||||
const $radioButtons = $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.
|
||||
elementor.channels.editor
|
||||
.off( 'WPFormsDeleteThemeButtonClick' )
|
||||
.on( 'WPFormsDeleteThemeButtonClick', () => {
|
||||
app.deleteThemeModal( editor.model.attributes.settings.attributes, editor );
|
||||
} );
|
||||
|
||||
// Listen for the theme name change.
|
||||
editor.model.get( 'settings' )
|
||||
.off( 'change:customThemeName' )
|
||||
.on( 'change:customThemeName', function( model ) {
|
||||
const newName = model.get( 'customThemeName' );
|
||||
|
||||
app.changeThemeName( newName, model );
|
||||
app.updateThemesList( editor, $themesControl );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Maybe migrate to the custom theme.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*
|
||||
* @param {Object} settingsModel Settings model.
|
||||
* @param {Object} $themesControl Themes Control object.
|
||||
* @param {Object} editor Editor object.
|
||||
*/
|
||||
maybeMigrateToCustomTheme( settingsModel, $themesControl, editor ) {
|
||||
const previousSettings = settingsModel._previousAttributes;
|
||||
const atts = settingsModel.attributes;
|
||||
|
||||
if ( 'copyPasteJsonValue' in previousSettings && ! previousSettings.wpformsTheme && ! atts.isCustomTheme ) {
|
||||
const currentStyles = app.getCurrentStyleAttributes( settingsModel.attributes );
|
||||
app.createCustomTheme( settingsModel.attributes, currentStyles, true );
|
||||
app.updateThemesList( editor, $themesControl );
|
||||
}
|
||||
|
||||
settingsModel.setExternalChange( {
|
||||
isMigrated: 'true',
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Maybe update the custom theme settings.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*
|
||||
* @param {Object} model Settings model.
|
||||
*/
|
||||
maybeUpdateCustomTheme( model ) {
|
||||
const atts = model.attributes;
|
||||
const isCustomTheme = atts.isCustomTheme === 'true';
|
||||
|
||||
if ( ! isCustomTheme ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const changedAtts = model.changed;
|
||||
const allowedKeys = WPFormsElementorModern.getStyleAttributesKeys();
|
||||
|
||||
// Update only allowed attributes.
|
||||
for ( const element in changedAtts ) {
|
||||
if ( ! allowedKeys.includes( element ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const attrValue = WPFormsElementorModern.prepareComplexAttrValues( changedAtts[ element ], element );
|
||||
|
||||
app.updateCustomThemeAttribute( element, attrValue, atts );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the Themes control markup.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*
|
||||
* @param {string} selectedTheme Selected theme slug.
|
||||
*
|
||||
* @return {string} Themes items HTML.
|
||||
*/
|
||||
// eslint-disable-next-line complexity
|
||||
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-elementor-themes-radio-group">
|
||||
${ itemsHtml }
|
||||
</div>`;
|
||||
|
||||
return html;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the Themes list item markup.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*
|
||||
* @param {Object} theme Theme properties.
|
||||
* @param {string} slug Theme slug.
|
||||
* @param {string} selectedTheme Selected theme slug.
|
||||
*
|
||||
* @return {string} Themes items HTML.
|
||||
*/
|
||||
getThemesItemMarkup( theme, slug, selectedTheme ) {
|
||||
if ( ! theme ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const title = theme.name?.length > 0 ? theme.name : strings.theme_noname;
|
||||
let radioClasses = 'wpforms-elementor-themes-radio ';
|
||||
const buttonClass = slug === selectedTheme ? 'is-active' : '';
|
||||
|
||||
radioClasses += app.isDisabledTheme( slug ) ? 'wpforms-elementor-themes-radio-disabled' : ' wpforms-elementor-themes-radio-enabled';
|
||||
|
||||
return `<button type="button" class="${ buttonClass }" value="${ slug }" role="radio">
|
||||
<div class="wpforms-elementor-themes-radio ${ radioClasses }">
|
||||
<div class="wpforms-elementor-themes-radio-title">${ title }</div>
|
||||
</div>
|
||||
|
||||
<div class="wpforms-elementor-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>`;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get theme data.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*
|
||||
* @param {string} slug Theme slug.
|
||||
*
|
||||
* @return {Object|null} Theme settings.
|
||||
*/
|
||||
getTheme( slug ) {
|
||||
return app.getAllThemes()[ slug ] || null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get enabled themes data.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*
|
||||
* @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.6
|
||||
*
|
||||
* @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.6
|
||||
*
|
||||
* @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.6
|
||||
*
|
||||
* @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 API.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*/
|
||||
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 + 'elementor/themes/',
|
||||
method: 'GET',
|
||||
cache: 'no-cache',
|
||||
} )
|
||||
.then( ( response ) => {
|
||||
themesData.wpforms = response.wpforms || {};
|
||||
themesData.custom = response.custom || {};
|
||||
} )
|
||||
.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 the custom themes.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*/
|
||||
saveCustomThemes() {
|
||||
if ( ! isAdmin ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Custom themes do not exist.
|
||||
if ( state.isSavingThemes || ! themesData.custom ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the flag to true indicating a saving is in progress.
|
||||
state.isSavingThemes = true;
|
||||
|
||||
try {
|
||||
// Save themes.
|
||||
wp.apiFetch( {
|
||||
path: routeNamespace + 'elementor/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.6
|
||||
*
|
||||
* @param {Object} atts Widget attributes.
|
||||
*
|
||||
* @return {Object} Whether the custom theme is created.
|
||||
*/
|
||||
getCurrentStyleAttributes( atts ) {
|
||||
const defaultAttributes = Object.keys( themesData.wpforms.default?.settings );
|
||||
|
||||
const currentStyleAttributes = {};
|
||||
|
||||
for ( const key in defaultAttributes ) {
|
||||
const attr = defaultAttributes[ key ];
|
||||
currentStyleAttributes[ attr ] = WPFormsElementorModern.prepareComplexAttrValues( atts[ attr ], defaultAttributes[ key ] ) ?? '';
|
||||
}
|
||||
|
||||
return currentStyleAttributes;
|
||||
},
|
||||
|
||||
/**
|
||||
* Maybe create a custom theme.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*
|
||||
* @param {Object} atts Widget attributes.
|
||||
*
|
||||
* @return {boolean} Whether the custom theme is created.
|
||||
*/
|
||||
// eslint-disable-next-line complexity
|
||||
maybeCreateCustomTheme( atts ) {
|
||||
const currentStyles = app.getCurrentStyleAttributes( atts );
|
||||
const isWPFormsTheme = !! themesData.wpforms[ atts.wpformsTheme ];
|
||||
const isCustomTheme = !! themesData.custom[ atts.wpformsTheme ];
|
||||
|
||||
// It is one of the default themes without any changes.
|
||||
if (
|
||||
isWPFormsTheme &&
|
||||
JSON.stringify( themesData.wpforms[ atts.wpformsTheme ]?.settings ) === JSON.stringify( currentStyles )
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// It is a modified default theme OR unknown custom theme.
|
||||
if ( isWPFormsTheme || ! isCustomTheme ) {
|
||||
app.createCustomTheme( atts, currentStyles );
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a custom theme.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*
|
||||
* @param {Object} atts Widget properties.
|
||||
* @param {Object} currentStyles Current style settings.
|
||||
* @param {boolean} migrateToCustomTheme Whether it is necessary to migrate to custom theme.
|
||||
*
|
||||
* @return {boolean} Whether the custom theme is created.
|
||||
*/
|
||||
createCustomTheme( atts, currentStyles = null, migrateToCustomTheme = false ) { // eslint-disable-line complexity
|
||||
let counter = 0;
|
||||
let themeSlug = atts.wpformsTheme;
|
||||
|
||||
const baseTheme = app.getTheme( atts.wpformsTheme ) || themesData.wpforms.default;
|
||||
let themeName = baseTheme.name;
|
||||
|
||||
themesData.custom = themesData.custom || {};
|
||||
|
||||
if ( migrateToCustomTheme ) {
|
||||
themeSlug = 'custom';
|
||||
themeName = strings.theme_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 + ')';
|
||||
|
||||
// The first migrated Custom Theme should be without a ` (Copy)` suffix.
|
||||
themeName = migrateToCustomTheme && counter < 2 ? strings.theme_custom : themeName;
|
||||
|
||||
// Add the new custom theme.
|
||||
themesData.custom[ themeSlug ] = {
|
||||
name: themeName,
|
||||
settings: currentStyles || app.getCurrentStyleAttributes( atts ),
|
||||
};
|
||||
|
||||
app.updateEnabledThemes( themeSlug, themesData.custom[ themeSlug ] );
|
||||
|
||||
const widget = elementor.getPanelView().getCurrentPageView().getOption( 'editedElementView' );
|
||||
const settingsModel = widget.model.get( 'settings' );
|
||||
|
||||
settingsModel.setExternalChange( {
|
||||
wpformsTheme: themeSlug,
|
||||
isCustomTheme: 'true',
|
||||
customThemeName: themeName,
|
||||
} );
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Maybe create a custom theme by given attributes.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*
|
||||
* @param {Object} attributes Widget attributes.
|
||||
*
|
||||
* @return {string} New theme's slug.
|
||||
*/
|
||||
maybeCreateCustomThemeFromAttributes( attributes ) { // eslint-disable-line complexity
|
||||
const newThemeSlug = attributes.theme;
|
||||
/**
|
||||
* @type {Object|null}
|
||||
* @property {Object} settings Theme settings.
|
||||
*/
|
||||
const existingTheme = app.getTheme( attributes.theme );
|
||||
const keys = Object.keys( attributes );
|
||||
|
||||
let isExistingTheme = Boolean( existingTheme?.settings );
|
||||
|
||||
// Check if the theme already exists and has the same settings.
|
||||
if ( isExistingTheme ) {
|
||||
for ( const i in keys ) {
|
||||
const key = keys[ i ];
|
||||
|
||||
if ( ! existingTheme.settings[ key ] || existingTheme.settings[ key ] !== attributes[ key ] ) {
|
||||
isExistingTheme = false;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The theme exists and has the same settings.
|
||||
if ( isExistingTheme ) {
|
||||
return newThemeSlug;
|
||||
}
|
||||
|
||||
// The theme doesn't exist.
|
||||
// Normalize the attributes to the default theme settings.
|
||||
const defaultAttributes = Object.keys( themesData.wpforms.default.settings );
|
||||
const newSettings = {};
|
||||
|
||||
for ( const i in defaultAttributes ) {
|
||||
const attr = defaultAttributes[ i ];
|
||||
|
||||
newSettings[ attr ] = attributes[ attr ] ?? '';
|
||||
}
|
||||
|
||||
// Create a new custom theme.
|
||||
themesData.custom[ newThemeSlug ] = {
|
||||
name: attributes.themeName ?? strings.theme_custom,
|
||||
settings: newSettings,
|
||||
};
|
||||
|
||||
app.updateEnabledThemes( newThemeSlug, themesData.custom[ newThemeSlug ] );
|
||||
|
||||
return newThemeSlug;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update custom theme.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*
|
||||
* @param {string} attribute Attribute name.
|
||||
* @param {string} value New attribute value.
|
||||
* @param {Object} atts Widget properties.
|
||||
*/
|
||||
updateCustomThemeAttribute( attribute, value, atts ) { // eslint-disable-line complexity
|
||||
const themeSlug = atts.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;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the widget theme.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*
|
||||
* @param {string} themeSlug The theme slug.
|
||||
*
|
||||
* @return {boolean} True on success.
|
||||
*/
|
||||
setWidgetTheme( themeSlug ) { // eslint-disable-line complexity
|
||||
if ( app.maybeDisplayUpgradeModal( themeSlug ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const theme = app.getTheme( themeSlug );
|
||||
|
||||
if ( ! theme?.settings ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const attributes = Object.keys( theme.settings );
|
||||
const widget = elementor.getPanelView().getCurrentPageView().getOption( 'editedElementView' );
|
||||
const settingsModel = widget.model.get( 'settings' );
|
||||
const isCustomTheme = !! themesData.custom[ themeSlug ];
|
||||
|
||||
// Set the theme attribute.
|
||||
settingsModel.setExternalChange( {
|
||||
wpformsTheme: themeSlug,
|
||||
isCustomTheme: isCustomTheme ? 'true' : '',
|
||||
customThemeName: isCustomTheme ? themesData.custom[ themeSlug ].name : '',
|
||||
} );
|
||||
|
||||
// Clean up the attributes.
|
||||
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, settingsModel );
|
||||
|
||||
// Activate the Publish button.
|
||||
const $pageView = elementor.getPanelView().getCurrentPageView().$el;
|
||||
$pageView.find( '.elementor-control-isCustomTheme input' ).trigger( 'input' );
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update styles atts.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*
|
||||
* @param {Object} themeSettings Theme settings.
|
||||
* @param {Object} settingsModel Settings model.
|
||||
*/
|
||||
// eslint-disable-next-line complexity
|
||||
updateStylesAtts( themeSettings, settingsModel ) {
|
||||
const allowedKeys = WPFormsElementorModern.getStyleAttributesKeys();
|
||||
const validSettings = {};
|
||||
|
||||
for ( const key in themeSettings ) {
|
||||
if ( ! allowedKeys.includes( key ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let value = themeSettings[ key ];
|
||||
|
||||
if ( key === 'backgroundUrl' && typeof value === 'string' ) {
|
||||
const match = value.match( /^url\(\s*['"]?(.*?)['"]?\s*\)$/i );
|
||||
if ( match && match[ 1 ] ) {
|
||||
value = { id: '', url: match[ 1 ] };
|
||||
} else {
|
||||
value = '';
|
||||
}
|
||||
}
|
||||
|
||||
validSettings[ key ] = value;
|
||||
}
|
||||
|
||||
// Update the widget settings.
|
||||
if ( Object.keys( validSettings ).length ) {
|
||||
settingsModel.setExternalChange( validSettings );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Maybe display upgrades modal in Lite.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*
|
||||
* @param {string} themeSlug The theme slug.
|
||||
*
|
||||
* @return {boolean} True if modal was displayed.
|
||||
*/
|
||||
maybeDisplayUpgradeModal( themeSlug ) {
|
||||
if ( ! app.isDisabledTheme( themeSlug ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! isPro ) {
|
||||
WPFormsElementorModern.showProModal( 'themes', strings.form_themes );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( ! isLicenseActive ) {
|
||||
WPFormsElementorModern.showLicenseModal( strings.form_themes );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Select widget theme event handler.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*
|
||||
* @param {string} value New attribute value.
|
||||
*/
|
||||
selectTheme( value ) {
|
||||
if ( ! app.setWidgetTheme( value ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
app.onSelectThemeWithBG( value );
|
||||
},
|
||||
|
||||
/**
|
||||
* Change theme name event handler.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*
|
||||
* @param {string} value New attribute value.
|
||||
* @param {Object} model Model object.
|
||||
*/
|
||||
changeThemeName( value, model ) {
|
||||
app.updateCustomThemeAttribute( 'themeName', value, model.attributes );
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the theme delete confirmation window.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*
|
||||
* @param {Object} atts Widget properties.
|
||||
* @param {Object} editor Editor object.
|
||||
*/
|
||||
deleteThemeModal( atts, editor ) {
|
||||
const themeName = app.getTheme( atts.wpformsTheme )?.name;
|
||||
const confirm = strings.theme_delete_confirm.replace( '%1$s', `<b>${ themeName }</b>` );
|
||||
const content = `<p class="wpforms-theme-delete-text">${ confirm } ${ strings.theme_delete_cant_undone }</p>`;
|
||||
const $panelContent = editor.$childViewContainer[ 0 ];
|
||||
const $themesControl = $( $panelContent ).find( '.wpforms-elementor-themes-control' );
|
||||
|
||||
const dialog = elementorCommon.dialogsManager.createWidget( 'confirm', {
|
||||
message: content,
|
||||
headerMessage: strings.theme_delete_title,
|
||||
|
||||
onConfirm: () => {
|
||||
// Remove theme from the theme storage.
|
||||
delete themesData.custom[ atts.wpformsTheme ];
|
||||
app.selectTheme( 'default' );
|
||||
app.updateThemesList( editor, $themesControl );
|
||||
},
|
||||
} );
|
||||
|
||||
dialog.show();
|
||||
},
|
||||
|
||||
/**
|
||||
* Open stock photos install modal on the select theme.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*
|
||||
* @param {string} themeSlug The theme slug.
|
||||
*/
|
||||
onSelectThemeWithBG( themeSlug ) {
|
||||
if ( WPFormsElementorModern.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()' ) {
|
||||
WPFormsElementorModern.stockPhotos.installModal( 'themes' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}( document, window, jQuery ) );
|
||||
|
||||
// Initialize.
|
||||
WPFormsElementorThemes.init();
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
var WPFormsElementorThemes=window.WPFormsElementorThemes||((e,m)=>{let{isAdmin:t,isPro:r,isLicenseActive:o,strings:u,route_namespace:s}=wpformsElementorVars,n={},h={wpforms:null,custom:null},a=null,i={},c={init(){i.$window=m(e),c.fetchThemesData(),c.events()},events(){m(e).on("elementor/frontend/init",function(){elementor.channels.editor.on("section:activated",c.themesControlSetup)})},getAllThemes(){return{...h.custom||{},...h.wpforms||{}}},themesControlSetup(e,t){"themes"===e&&"wpforms"===t.model.attributes.widgetType&&(e=t.$childViewContainer[0],e=m(e).find(".wpforms-elementor-themes-control"),c.isMac()&&e.addClass("wpforms-is-mac"),c.updateThemesList(t,e))},updateThemesList(e,t){var s=e.model.attributes.settings.attributes.wpformsTheme??"default",s=c.getThemesListMarkup(s);t.html(s),c.addThemesEvents(t,e)},addThemesEvents(s,r){let t=_.debounce(e=>{c.maybeCreateCustomTheme(e)},300);var e=r.model.get("settings");"true"!==e.attributes.isMigrated&&c.maybeMigrateToCustomTheme(e,s,r),e.on("change",e=>{t(e.attributes),c.maybeUpdateCustomTheme(e)});let o=s.find('[role="radio"]');o.off("click").on("click",function(){o.removeClass("is-active"),m(this).addClass("is-active");var e=m(this).val();c.selectTheme(e)}),elementor.channels.editor.off("WPFormsDeleteThemeButtonClick").on("WPFormsDeleteThemeButtonClick",()=>{c.deleteThemeModal(r.model.attributes.settings.attributes,r)}),r.model.get("settings").off("change:customThemeName").on("change:customThemeName",function(e){var t=e.get("customThemeName");c.changeThemeName(t,e),c.updateThemesList(r,s)})},maybeMigrateToCustomTheme(e,t,s){var r=e._previousAttributes,o=e.attributes;"copyPasteJsonValue"in r&&!r.wpformsTheme&&!o.isCustomTheme&&(r=c.getCurrentStyleAttributes(e.attributes),c.createCustomTheme(e.attributes,r,!0),c.updateThemesList(s,t)),e.setExternalChange({isMigrated:"true"})},maybeUpdateCustomTheme(e){var t=e.attributes;if("true"===t.isCustomTheme){var s,r,o=e.changed,m=WPFormsElementorModern.getStyleAttributesKeys();for(s in o)m.includes(s)&&(r=WPFormsElementorModern.prepareComplexAttrValues(o[s],s),c.updateCustomThemeAttribute(s,r,t))}},getThemesListMarkup(e){if(!h.wpforms)return c.fetchThemesData(),`<div class="wpforms-no-themes">${u.themes_error}</div>`;var t=c.getAllThemes();if(!t)return"";var s=Object.keys(t);let r,o;let m="";for(var n in c.isWPFormsTheme(e)||(o=e,m+=c.getThemesItemMarkup(c.getTheme(o),o,o)),s){n=s[n];o&&o===n||((r={...t.default,...t[n]||{}}).settings={...t.default.settings,...r.settings||{}},m+=c.getThemesItemMarkup(r,n,e))}return`<div role="radiogroup" class="wpforms-elementor-themes-radio-group">
|
||||
${m}
|
||||
</div>`},getThemesItemMarkup(e,t,s){var r,o;return e?(r=0<e.name?.length?e.name:u.theme_noname,o="wpforms-elementor-themes-radio ",`<button type="button" class="${t===s?"is-active":""}" value="${t}" role="radio">
|
||||
<div class="wpforms-elementor-themes-radio ${o+=c.isDisabledTheme(t)?"wpforms-elementor-themes-radio-disabled":" wpforms-elementor-themes-radio-enabled"}">
|
||||
<div class="wpforms-elementor-themes-radio-title">${r}</div>
|
||||
</div>
|
||||
|
||||
<div class="wpforms-elementor-themes-indicators">
|
||||
<span class="component-color-indicator" title="${u.button_background}" style="background: ${e.settings.buttonBackgroundColor};" data-index="0"></span>
|
||||
<span class="component-color-indicator" title="${u.button_text}" style="background: ${e.settings.buttonTextColor}" data-index="1"></span>
|
||||
<span class="component-color-indicator" title="${u.field_label}" style="background: ${e.settings.labelColor};" data-index="2"></span>
|
||||
<span class="component-color-indicator" title="${u.field_sublabel} " style="background: ${e.settings.labelSublabelColor};" data-index="3"></span>
|
||||
<span class="component-color-indicator" title="${u.field_border}" style="background: ${e.settings.fieldBorderColor};" data-index="4"></span>
|
||||
</div>
|
||||
</button>`):""},getTheme(e){return c.getAllThemes()[e]||null},getEnabledThemes(){if(!a){let s=c.getAllThemes();if(r&&o)return s;a=Object.keys(s).reduce((e,t)=>(s[t].settings?.fieldSize&&!s[t].disabled&&(e[t]=s[t]),e),{})}return a},updateEnabledThemes(e,t){a=a&&{...a,[e]:t}},isDisabledTheme(e){return!c.getEnabledThemes()?.[e]},isWPFormsTheme(e){return Boolean(h.wpforms[e]?.settings)},fetchThemesData(){if(!n.isFetchingThemes&&!h.wpforms){n.isFetchingThemes=!0;try{wp.apiFetch({path:s+"elementor/themes/",method:"GET",cache:"no-cache"}).then(e=>{h.wpforms=e.wpforms||{},h.custom=e.custom||{}}).catch(e=>{console.error(e?.message)}).finally(()=>{n.isFetchingThemes=!1})}catch(e){console.error(e)}}},saveCustomThemes(){if(t&&!n.isSavingThemes&&h.custom){n.isSavingThemes=!0;try{wp.apiFetch({path:s+"elementor/themes/custom/",method:"POST",data:{customThemes:h.custom}}).then(e=>{e?.result||console.log(e?.error)}).catch(e=>{console.error(e?.message)}).finally(()=>{n.isSavingThemes=!1})}catch(e){console.error(e)}}},getCurrentStyleAttributes(e){var t,s=Object.keys(h.wpforms.default?.settings),r={};for(t in s){var o=s[t];r[o]=WPFormsElementorModern.prepareComplexAttrValues(e[o],s[t])??""}return r},maybeCreateCustomTheme(e){var t=c.getCurrentStyleAttributes(e),s=!!h.wpforms[e.wpformsTheme],r=!!h.custom[e.wpformsTheme];return!(s&&JSON.stringify(h.wpforms[e.wpformsTheme]?.settings)===JSON.stringify(t)||(!s&&r||c.createCustomTheme(e,t),0))},createCustomTheme(e,t=null,s=!1){let r=0,o=e.wpformsTheme;let m=(c.getTheme(e.wpformsTheme)||h.wpforms.default).name;for(h.custom=h.custom||{},s&&(o="custom",m=u.theme_custom);r++,o=o+"-copy-"+r,h.custom[o]&&r<1e4;);var n=r<2?u.theme_copy:u.theme_copy+" "+r;return m+=" ("+n+")",m=s&&r<2?u.theme_custom:m,h.custom[o]={name:m,settings:t||c.getCurrentStyleAttributes(e)},c.updateEnabledThemes(o,h.custom[o]),elementor.getPanelView().getCurrentPageView().getOption("editedElementView").model.get("settings").setExternalChange({wpformsTheme:o,isCustomTheme:"true",customThemeName:m}),!0},maybeCreateCustomThemeFromAttributes(e){var t=e.theme,s=c.getTheme(e.theme),r=Object.keys(e);let o=Boolean(s?.settings);if(o)for(var m in r){m=r[m];if(!s.settings[m]||s.settings[m]!==e[m]){o=!1;break}}if(!o){var n,a=Object.keys(h.wpforms.default.settings),i={};for(n in a){var l=a[n];i[l]=e[l]??""}h.custom[t]={name:e.themeName??u.theme_custom,settings:i},c.updateEnabledThemes(t,h.custom[t])}return t},updateCustomThemeAttribute(e,t,s){s=s.wpformsTheme;h.wpforms[s]||"themeName"!==e&&!h.wpforms.default.settings[e]||h.custom[s]&&("themeName"===e?h.custom[s].name=t:(h.custom[s].settings=h.custom[s].settings||h.wpforms.default.settings,h.custom[s].settings[e]=t))},setWidgetTheme(e){if(c.maybeDisplayUpgradeModal(e))return!1;var t=c.getTheme(e);if(!t?.settings)return!1;var s,r=Object.keys(t.settings),o=elementor.getPanelView().getCurrentPageView().getOption("editedElementView").model.get("settings"),m=!!h.custom[e],n=(o.setExternalChange({wpformsTheme:e,isCustomTheme:m?"true":"",customThemeName:m?h.custom[e].name:""}),{});for(s in r){var a=r[s],i=t.settings[a];n[a]="string"==typeof i?i.replace(/px$/,""):i}return c.updateStylesAtts(n,o),elementor.getPanelView().getCurrentPageView().$el.find(".elementor-control-isCustomTheme input").trigger("input"),!0},updateStylesAtts(t,e){var s,r,o=WPFormsElementorModern.getStyleAttributesKeys(),m={};for(s in t)if(o.includes(s)){let e=t[s];"backgroundUrl"===s&&"string"==typeof e&&(r=e.match(/^url\(\s*['"]?(.*?)['"]?\s*\)$/i),e=r&&r[1]?{id:"",url:r[1]}:""),m[s]=e}Object.keys(m).length&&e.setExternalChange(m)},maybeDisplayUpgradeModal(e){return!(!c.isDisabledTheme(e)||(r?o||(WPFormsElementorModern.showLicenseModal(u.form_themes),0):(WPFormsElementorModern.showProModal("themes",u.form_themes),0)))},selectTheme(e){c.setWidgetTheme(e)&&c.onSelectThemeWithBG(e)},changeThemeName(e,t){c.updateCustomThemeAttribute("themeName",e,t.attributes)},deleteThemeModal(e,t){var s=c.getTheme(e.wpformsTheme)?.name,s=`<p class="wpforms-theme-delete-text">${u.theme_delete_confirm.replace("%1$s",`<b>${s}</b>`)} ${u.theme_delete_cant_undone}</p>`,r=t.$childViewContainer[0];let o=m(r).find(".wpforms-elementor-themes-control");elementorCommon.dialogsManager.createWidget("confirm",{message:s,headerMessage:u.theme_delete_title,onConfirm:()=>{delete h.custom[e.wpformsTheme],c.selectTheme("default"),c.updateThemesList(t,o)}}).show()},onSelectThemeWithBG(e){WPFormsElementorModern.stockPhotos.isPicturesAvailable()||c.isWPFormsTheme(e)&&(e=c.getTheme(e).settings?.backgroundUrl)?.length&&"url()"!==e&&WPFormsElementorModern.stockPhotos.installModal("themes")},isMac(){return navigator.userAgent.includes("Macintosh")}};return c})((document,window),jQuery);WPFormsElementorThemes.init();
|
||||
Reference in New Issue
Block a user