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:
+486
@@ -0,0 +1,486 @@
|
||||
/* global wpforms_builder, wpf, WPFormsBuilder, WPForms, md5 */
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* WPForms Internal Information Field builder functions.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*/
|
||||
var WPFormsInternalInformationField = window.WPFormsInternalInformationField || ( function( document, window, $ ) { // eslint-disable-line
|
||||
|
||||
/**
|
||||
* WPForms builder element.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @type {jQuery}
|
||||
*/
|
||||
let $builder;
|
||||
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @type {object}
|
||||
*/
|
||||
let app = {
|
||||
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*/
|
||||
init: function() {
|
||||
|
||||
$( app.ready );
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialized once the DOM is fully loaded.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*/
|
||||
ready: function() {
|
||||
|
||||
$builder = $( '#wpforms-builder' );
|
||||
|
||||
app.bindUIActionsFields();
|
||||
},
|
||||
|
||||
/**
|
||||
* Element bindings.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*/
|
||||
bindUIActionsFields: function() {
|
||||
|
||||
app.dragDisable();
|
||||
|
||||
$builder
|
||||
.on( 'wpformsFieldAdd', app.dragDisable )
|
||||
.on( 'input', '.wpforms-field-option-row-heading input[type="text"]', app.headingUpdates )
|
||||
.on( 'input', '.wpforms-field-option-row-expanded-description textarea', app.expandedDescriptionUpdates )
|
||||
.on( 'input', '.wpforms-field-option-row-cta-label input[type="text"]', app.ctaButtonLabelUpdates )
|
||||
.on( 'input', '.wpforms-field-option-row-cta-link input[type="text"]', app.ctaButtonLinkUpdates )
|
||||
.on( 'click', '.cta-button.cta-expand-description a', app.showExpandedDescription )
|
||||
.on( 'focusout', '.wpforms-field-option-row-cta-link input[type="text"]', app.validateCTAlinkField )
|
||||
.on( 'mousedown', '.wpforms-field-internal-information-checkbox', app.handleCheckboxClick )
|
||||
.on( 'wpformsDescriptionFieldUpdated', app.descriptionFieldUpdated )
|
||||
.on( 'wpformsBeforeFieldDeleteAlert', app.preventDeleteFieldAlert )
|
||||
.on( 'mouseenter', '.internal-information-not-editable .wpforms-field-delete', app.showDismissTitle );
|
||||
},
|
||||
|
||||
/**
|
||||
* Save checkbox state as a post meta.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param {string} name Checkbox name.
|
||||
* @param {int} checked Checkbox state.
|
||||
*/
|
||||
saveInternalInformationCheckbox: function( name, checked ) {
|
||||
|
||||
$.post(
|
||||
wpforms_builder.ajax_url,
|
||||
{
|
||||
action: 'wpforms_builder_save_internal_information_checkbox',
|
||||
formId: $( '#wpforms-builder-form' ).data( 'id' ),
|
||||
name: name,
|
||||
checked: checked,
|
||||
nonce: wpforms_builder.nonce,
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Replace checkboxes.
|
||||
*
|
||||
* @since 1.7.6
|
||||
* @since 1.7.9 Added ID parameter.
|
||||
*
|
||||
* @param {string} description Expanded description.
|
||||
* @param {int} id Field ID.
|
||||
*
|
||||
* @returns {string} Expanded description with checkboxes HTML.
|
||||
*/
|
||||
replaceCheckboxes: function( description, id ) {
|
||||
|
||||
const lines = description.split( /\r?\n/ ),
|
||||
replaced = [],
|
||||
needle = '[] ';
|
||||
|
||||
let lineNumber = -1;
|
||||
|
||||
for ( let line of lines ) {
|
||||
|
||||
lineNumber++;
|
||||
line = line.trim();
|
||||
|
||||
if ( ! line.startsWith( needle ) ) {
|
||||
replaced.push( line );
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
const hash = md5( line ),
|
||||
name = `iif-${id}-${hash}-${lineNumber}`;
|
||||
|
||||
line = line.replace( '[] ', `<div class="wpforms-field-internal-information-checkbox-wrap"><div class="wpforms-field-internal-information-checkbox-input"><input type="checkbox" name="${name}" value="1" class="wpforms-field-internal-information-checkbox" /></div><div class="wpforms-field-internal-information-checkbox-label">` ); line += '</div></div>';
|
||||
|
||||
replaced.push( line );
|
||||
}
|
||||
|
||||
return ( wpf.wpautop( replaced.join( '\n' ) ) ).replace( /<br \/>\n$/, '' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Do not allow field to be draggable.
|
||||
*
|
||||
* @since 1.7.9
|
||||
*/
|
||||
dragDisable: function() {
|
||||
|
||||
WPForms.Admin.Builder.DragFields.fieldDragDisable( $( '.internal-information-not-draggable' ), false );
|
||||
},
|
||||
|
||||
/**
|
||||
* Real-time updates for "Heading" field option.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*/
|
||||
headingUpdates: function() {
|
||||
|
||||
let $this = $( this ),
|
||||
value = wpf.sanitizeHTML( $this.val() ),
|
||||
$head = $( '#wpforms-field-' + $this.parent().data( 'field-id' ) ).find( '.wpforms-field-internal-information-row-heading .heading' );
|
||||
|
||||
$head.toggle( value.length !== 0 );
|
||||
WPFormsBuilder.updateDescription( $head.find( '.text' ), value );
|
||||
},
|
||||
|
||||
/**
|
||||
* Real-time updates for "Expanded Description" field option.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*/
|
||||
expandedDescriptionUpdates: function() {
|
||||
|
||||
const $this = $( this ),
|
||||
value = wpf.sanitizeHTML( $this.val() ),
|
||||
id = $this.parent().data( 'field-id' ),
|
||||
$field = $( '#wpforms-field-' + id ),
|
||||
$wrapper = $field.find( '.internal-information-wrap' ),
|
||||
$buttonContainer = $field.find( '.wpforms-field-internal-information-row-cta-button' ),
|
||||
$options = $( '#wpforms-field-option-' + id ),
|
||||
link = $options.find( '.wpforms-field-option-row-cta-link input[type="text"]' ).val(),
|
||||
label = $options.find( '.wpforms-field-option-row-cta-label input[type="text"]' ).val().length !== 0 ? $options.find( '.wpforms-field-option-row-cta-label input[type="text"]' ).val() : wpforms_builder.empty_label,
|
||||
$expandable = $wrapper.find( '.wpforms-field-internal-information-row-expanded-description' );
|
||||
|
||||
const newLines = app.replaceCheckboxes( value, id );
|
||||
|
||||
WPFormsBuilder.updateDescription( $wrapper.find( '.expanded-description' ), newLines );
|
||||
|
||||
if ( value.length !== 0 ) { // Expanded description has content.
|
||||
if ( $expandable.hasClass( 'expanded' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update CTA button.
|
||||
$buttonContainer.html( app.notExpandedButton() );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$expandable.hide().removeClass( 'expanded' );
|
||||
|
||||
if ( link.length === 0 ) { // Expanded description does not have value and button has no link.
|
||||
$buttonContainer.html( '' );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$buttonContainer.html( app.standardCtaButton( link, label ) );
|
||||
},
|
||||
|
||||
/**
|
||||
* Expand additional description on button click.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param {object} event Click event.
|
||||
*/
|
||||
showExpandedDescription: function( event ) {
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
const $this = $( this ),
|
||||
id = $this.closest( '.wpforms-field-internal-information' ).data( 'field-id' ),
|
||||
$expandable = $this.closest( '.internal-information-content' ).find( '.wpforms-field-internal-information-row-expanded-description' ),
|
||||
$buttonContainer = $( '#wpforms-field-' + id ).find( '.wpforms-field-internal-information-row-cta-button' ),
|
||||
isExpanded = $expandable.hasClass( 'expanded' );
|
||||
|
||||
$expandable.toggleClass( 'expanded' );
|
||||
|
||||
if ( ! isExpanded ) {
|
||||
$expandable.slideDown( 400 );
|
||||
$buttonContainer.html( app.expandedButton() );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$expandable.slideUp( 400 );
|
||||
$buttonContainer.html( app.notExpandedButton() );
|
||||
},
|
||||
|
||||
/**
|
||||
* Validate if the CTA Link field has correct url.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*/
|
||||
validateCTAlinkField: function() {
|
||||
|
||||
const $field = $( this ),
|
||||
url = $field.val().trim();
|
||||
|
||||
$field.val( url );
|
||||
|
||||
if ( url === '' || wpf.isURL( url ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.confirm(
|
||||
{
|
||||
title: wpforms_builder.heads_up,
|
||||
content: wpforms_builder.iif_redirect_url_field_error,
|
||||
icon: 'fa fa-exclamation-circle',
|
||||
type: 'orange',
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: wpforms_builder.ok,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
action: function() {
|
||||
$field.trigger( 'focus' );
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle checkbox checking.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param {object} event Click event.
|
||||
*/
|
||||
handleCheckboxClick: function( event ) {
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
const $this = $( this ),
|
||||
checked = ! $this.prop( 'checked' );
|
||||
|
||||
$this.prop( 'checked', checked );
|
||||
|
||||
app.saveInternalInformationCheckbox( $this.prop( 'name' ), Number( checked ) );
|
||||
},
|
||||
|
||||
/**
|
||||
* Replace checkboxes on description field.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param {object} event Triggered event.
|
||||
* @param {object} data Field element and field value.
|
||||
*/
|
||||
descriptionFieldUpdated: function( event, data ) {
|
||||
|
||||
const type = $( '#wpforms-field-' + data.id ).data( 'field-type' );
|
||||
|
||||
if ( type !== 'internal-information' || data.value.length === 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
data.value = app.replaceCheckboxes( data.value, data.id );
|
||||
|
||||
WPFormsBuilder.updateDescription( data.descField, data.value );
|
||||
},
|
||||
|
||||
/**
|
||||
* Prevent delete field alert to show.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param {object} event Triggered event.
|
||||
* @param {object} fieldData Field data.
|
||||
* @param {string} type Field type.
|
||||
*/
|
||||
preventDeleteFieldAlert: function( event, fieldData, type ) {
|
||||
|
||||
if ( type === 'internal-information' ) {
|
||||
event.preventDefault();
|
||||
WPFormsBuilder.fieldDeleteById( fieldData.id, type, 50 );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Replace Delete field button title with Dismiss.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*/
|
||||
showDismissTitle: function() {
|
||||
|
||||
$( this ).attr( 'title', wpforms_builder.iif_dismiss );
|
||||
},
|
||||
|
||||
/**
|
||||
* Real-time updates for "CTA button" link.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*/
|
||||
ctaButtonLinkUpdates() {
|
||||
|
||||
let $this = $( this ),
|
||||
id = $this.parent().data( 'field-id' ),
|
||||
$field = $( '#wpforms-field-' + id ),
|
||||
$buttonContainer = $field.find( '.wpforms-field-internal-information-row-cta-button' ),
|
||||
$expandable = $field.find( '.wpforms-field-internal-information-row-expanded-description' ),
|
||||
desc = $this.closest( '#wpforms-field-option-' + id ).find( '.wpforms-field-option-row-expanded-description textarea' ).val(),
|
||||
label = $this.closest( '#wpforms-field-option-' + id ).find( '.wpforms-field-option-row-cta-label input[type="text"]' ).val();
|
||||
|
||||
if ( desc.length !== 0 ) {
|
||||
|
||||
if ( $expandable.hasClass( 'expanded' ) ) {
|
||||
|
||||
$buttonContainer.html( app.expandedButton() );
|
||||
|
||||
return;
|
||||
}
|
||||
$buttonContainer.html( app.notExpandedButton() );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ( wpf.isURL( $this.val() ) && label.length !== 0 ) {
|
||||
$buttonContainer.html( app.standardCtaButton( $this.val(), label ) );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$buttonContainer.html( '' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Real-time updates for "CTA button" label.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*/
|
||||
ctaButtonLabelUpdates: function() {
|
||||
|
||||
let $this = $( this ),
|
||||
value = wpf.sanitizeHTML( $this.val() ),
|
||||
id = $this.parent().data( 'field-id' ),
|
||||
$field = $( '#wpforms-field-' + id ),
|
||||
$buttonContainer = $field.find( '.wpforms-field-internal-information-row-cta-button' ),
|
||||
$expandable = $field.find( '.wpforms-field-internal-information-row-expanded-description' ),
|
||||
desc = $this.closest( '#wpforms-field-option-' + id ).find( '.wpforms-field-option-row-expanded-description textarea' ).val(),
|
||||
link = $this.closest( '#wpforms-field-option-' + id ).find( '.wpforms-field-option-row-cta-link input[type="text"]' ).val();
|
||||
|
||||
if ( desc.length !== 0 && value.length !== 0 ) {
|
||||
if ( $expandable.hasClass( 'expanded' ) ) {
|
||||
|
||||
$buttonContainer.html( app.expandedButton() );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$buttonContainer.html( app.notExpandedButton() );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ( value.length !== 0 && wpf.isURL( link ) ) {
|
||||
$buttonContainer.html( app.standardCtaButton( link, value ) );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ( desc.length === 0 ) {
|
||||
$buttonContainer.html( '' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Standard CTA button template.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param {string} url Button URL.
|
||||
* @param {string} label Button label.
|
||||
*
|
||||
* @returns {string} Button HTML.
|
||||
*/
|
||||
standardCtaButton: function( url, label ) {
|
||||
|
||||
let button = `<div class="cta-button cta-link-external ">
|
||||
<a href="%url%" target="_blank" rel="noopener noreferrer">
|
||||
<span class="button-label">%label%</span>
|
||||
</a></div>`;
|
||||
|
||||
return button.replace( '%url%', wpf.sanitizeHTML( url ) ).replace( '%label%', wpf.sanitizeHTML( label ) );
|
||||
},
|
||||
|
||||
/**
|
||||
* Not expanded button.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @returns {string} Not expanded button HTML.
|
||||
*/
|
||||
notExpandedButton: function() {
|
||||
|
||||
let button = `<div class="cta-button cta-expand-description not-expanded">
|
||||
<a href="#" target="_blank" rel="noopener noreferrer">
|
||||
<span class="button-label">%label%</span>
|
||||
<span class="icon not-expanded">
|
||||
<svg viewBox="0 0 10 7">
|
||||
<path d="M4.91016 5.90234C5.15625 6.14844 5.56641 6.14844 5.8125 5.90234L9.53125 2.18359C9.80469 1.91016 9.80469 1.5 9.53125 1.25391L8.92969 0.625C8.65625 0.378906 8.24609 0.378906 8 0.625L5.34766 3.27734L2.72266 0.625C2.47656 0.378906 2.06641 0.378906 1.79297 0.625L1.19141 1.25391C0.917969 1.5 0.917969 1.91016 1.19141 2.18359L4.91016 5.90234Z"></path>
|
||||
<path d="M4.91016 5.90234C5.15625 6.14844 5.56641 6.14844 5.8125 5.90234L9.53125 2.18359C9.80469 1.91016 9.80469 1.5 9.53125 1.25391L8.92969 0.625C8.65625 0.378906 8.24609 0.378906 8 0.625L5.34766 3.27734L2.72266 0.625C2.47656 0.378906 2.06641 0.378906 1.79297 0.625L1.19141 1.25391C0.917969 1.5 0.917969 1.91016 1.19141 2.18359L4.91016 5.90234Z"></path>
|
||||
</svg>
|
||||
</span>
|
||||
</a></div>`;
|
||||
|
||||
return button.replace( '%label%', wpforms_builder.iif_more );
|
||||
},
|
||||
|
||||
/**
|
||||
* Expanded button.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @returns {string} Expanded button HTML.
|
||||
*/
|
||||
expandedButton: function() {
|
||||
|
||||
let button = `<div class="cta-button cta-expand-description expanded">
|
||||
<a href="#" target="_blank" rel="noopener noreferrer">
|
||||
<span class="button-label">%label%</span>
|
||||
<span class="icon expanded">
|
||||
<svg viewBox="0 0 10 7">
|
||||
<path d="M5.83984 0.625C5.56641 0.378906 5.15625 0.378906 4.91016 0.625L1.19141 4.34375C0.917969 4.61719 0.917969 5.02734 1.19141 5.27344L1.79297 5.90234C2.06641 6.14844 2.47656 6.14844 2.72266 5.90234L5.375 3.25L8 5.90234C8.24609 6.14844 8.68359 6.14844 8.92969 5.90234L9.55859 5.27344C9.80469 5.02734 9.80469 4.61719 9.55859 4.34375L5.83984 0.625Z" fill="red"></path>
|
||||
</svg>
|
||||
</span>
|
||||
</a></div>`;
|
||||
|
||||
return button.replace( '%label%', wpforms_builder.close );
|
||||
},
|
||||
};
|
||||
|
||||
return app;
|
||||
}( document, window, jQuery ) );
|
||||
|
||||
WPFormsInternalInformationField.init();
|
||||
Vendored
Executable
+21
File diff suppressed because one or more lines are too long
@@ -0,0 +1,317 @@
|
||||
/* global wpf */
|
||||
|
||||
/**
|
||||
* Form Builder Field Numbers module.
|
||||
*
|
||||
* @since 1.9.4
|
||||
*/
|
||||
var WPForms = window.WPForms || {}; // eslint-disable-line no-var
|
||||
|
||||
WPForms.Admin = WPForms.Admin || {};
|
||||
WPForms.Admin.Builder = WPForms.Admin.Builder || {};
|
||||
|
||||
WPForms.Admin.Builder.FieldNumbers = WPForms.Admin.Builder.FieldNumbers || ( function( document, window, $ ) { // eslint-disable-line
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.9.4
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const app = {
|
||||
|
||||
/**
|
||||
* WPForms builder element.
|
||||
*
|
||||
* @since 1.9.4
|
||||
*
|
||||
* @type {jQuery}
|
||||
*/
|
||||
$builder: null,
|
||||
|
||||
/**
|
||||
* Track if a tag was clicked recently.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
tagClicked: false,
|
||||
|
||||
/**
|
||||
* Initialize the application.
|
||||
*
|
||||
* @since 1.9.4
|
||||
*/
|
||||
init() {
|
||||
$( app.ready );
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the DOM is fully loaded.
|
||||
*
|
||||
* @since 1.9.4
|
||||
*/
|
||||
ready() {
|
||||
app.$builder = $( '#wpforms-builder' );
|
||||
app.numbersEvents();
|
||||
},
|
||||
|
||||
/**
|
||||
* Binds separate events for min, max, and default value inputs.
|
||||
*
|
||||
* @since 1.9.4
|
||||
*/
|
||||
numbersEvents() {
|
||||
app.$builder.on(
|
||||
'change',
|
||||
'.wpforms-field-option-number .wpforms-numbers-min',
|
||||
app.onChangeNumbersMin
|
||||
);
|
||||
|
||||
app.$builder.on(
|
||||
'change',
|
||||
'.wpforms-field-option-number .wpforms-numbers-max',
|
||||
app.onChangeNumbersMax
|
||||
);
|
||||
|
||||
app.$builder.on(
|
||||
'input',
|
||||
'.wpforms-field-option-number .wpforms-field-option-row-default_value .wpforms-smart-tags-widget-original',
|
||||
_.debounce( app.onChangeNumbersDefaultValue, 500 )
|
||||
);
|
||||
|
||||
app.$builder.on(
|
||||
'click',
|
||||
'.wpforms-smart-tags-widget .tag',
|
||||
app.smartTagClickTracking
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Track clicks on the smart tag bricks.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
smartTagClickTracking() {
|
||||
app.tagClicked = true;
|
||||
|
||||
// Reset the flag after a short delay.
|
||||
setTimeout( () => {
|
||||
app.tagClicked = false;
|
||||
}, 200 );
|
||||
},
|
||||
|
||||
/**
|
||||
* Parses the numeric value of a field, returning null if invalid or empty.
|
||||
*
|
||||
* @since 1.9.4
|
||||
*
|
||||
* @param {jQuery} $field The jQuery object for the input field.
|
||||
*
|
||||
* @return {number|null} The parsed numeric value or null.
|
||||
*/
|
||||
parseFieldValue( $field ) {
|
||||
if ( ! $field.length || $field.val() === '' ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const value = parseFloat( $field.val() );
|
||||
|
||||
return isNaN( value ) ? null : value;
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines if the min value is greater than the max value.
|
||||
*
|
||||
* @since 1.9.4
|
||||
*
|
||||
* @param {jQuery} $minField jQuery object for the min input field.
|
||||
* @param {jQuery} $maxField jQuery object for the max input field.
|
||||
*
|
||||
* @return {boolean} True if min is greater than max, otherwise false.
|
||||
*/
|
||||
isInvalidMinMaxRange( $minField, $maxField ) {
|
||||
const min = app.parseFieldValue( $minField ),
|
||||
max = app.parseFieldValue( $maxField );
|
||||
|
||||
return min !== null && max !== null && min > max;
|
||||
},
|
||||
|
||||
/**
|
||||
* Synchronizes the min attribute on the max field.
|
||||
*
|
||||
* @since 1.9.4
|
||||
*
|
||||
* @param {jQuery} $minField jQuery object for the min input field.
|
||||
* @param {jQuery} $maxField jQuery object for the max input field.
|
||||
*/
|
||||
syncNumberMinAttribute( $minField, $maxField ) {
|
||||
$maxField.attr( 'min', app.parseFieldValue( $minField ) );
|
||||
},
|
||||
|
||||
/**
|
||||
* Synchronizes the max attribute on the min field.
|
||||
*
|
||||
* @since 1.9.4
|
||||
*
|
||||
* @param {jQuery} $minField jQuery object for the min input field.
|
||||
* @param {jQuery} $maxField jQuery object for the max input field.
|
||||
*/
|
||||
syncNumberMaxAttribute( $minField, $maxField ) {
|
||||
$minField.attr( 'max', app.parseFieldValue( $maxField ) );
|
||||
},
|
||||
|
||||
/**
|
||||
* Adjusts the target field's value to match the source field's value.
|
||||
*
|
||||
* @since 1.9.4
|
||||
*
|
||||
* @param {jQuery} $sourceField jQuery object for the field with the value to copy.
|
||||
* @param {jQuery} $targetField jQuery object for the field to update.
|
||||
*/
|
||||
adjustValue( $sourceField, $targetField ) {
|
||||
$targetField.val( app.parseFieldValue( $sourceField ) ).trigger( 'input' ).trigger( 'wpformsSmartTagsInputSync' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the 'input' event for the min field, ensuring correct min <= max and default value.
|
||||
*
|
||||
* @since 1.9.4
|
||||
*
|
||||
* @param {Event} event The input event object.
|
||||
*/
|
||||
onChangeNumbersMin( event ) {
|
||||
const $minField = $( event.target ),
|
||||
$container = $minField.closest( '.wpforms-field-option-group' ),
|
||||
$maxField = $container.find( '.wpforms-numbers-max' ),
|
||||
$defaultValueField = $container.find( '.wpforms-field-option-row-default_value input.wpforms-smart-tags-widget-original' );
|
||||
|
||||
if ( app.isInvalidMinMaxRange( $minField, $maxField ) ) {
|
||||
app.adjustValue( $maxField, $minField );
|
||||
}
|
||||
|
||||
if ( app.isNeedAdjustDefaultValueByMinValue( $defaultValueField, $minField ) ) {
|
||||
app.adjustValue( $minField, $defaultValueField );
|
||||
}
|
||||
|
||||
app.syncNumberMinAttribute( $minField, $maxField );
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the 'change' event for the max field, ensuring correct min <= max and default value.
|
||||
*
|
||||
* @since 1.9.4
|
||||
*
|
||||
* @param {Event} event The change event object.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
onChangeNumbersMax( event ) {
|
||||
const $maxField = $( event.target ),
|
||||
$container = $maxField.closest( '.wpforms-field-option-group' ),
|
||||
$minField = $container.find( '.wpforms-numbers-min' ),
|
||||
$defaultValueField = $container.find( '.wpforms-field-option-row-default_value input.wpforms-smart-tags-widget-original' );
|
||||
|
||||
if ( app.isInvalidMinMaxRange( $minField, $maxField ) ) {
|
||||
app.adjustValue( $minField, $maxField );
|
||||
}
|
||||
|
||||
if ( app.isNeedAdjustDefaultValueByMaxValue( $defaultValueField, $maxField ) ) {
|
||||
app.adjustValue( $maxField, $defaultValueField );
|
||||
}
|
||||
|
||||
app.syncNumberMaxAttribute( $minField, $maxField );
|
||||
},
|
||||
|
||||
/**
|
||||
* Normalize a float value of the input field by replacing commas with dots.
|
||||
* If the normalized value differs from the original,
|
||||
* the input field will be updated and the 'input' event will be triggered.
|
||||
* Non-numeric values are ignored and remain unchanged.
|
||||
*
|
||||
* @since 1.9.4
|
||||
*
|
||||
* @param {jQuery} $field The input field to normalize.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
normalizeFloatValue( $field ) {
|
||||
const value = $field.val(),
|
||||
valueWithoutComma = value.replace( ',', '.' );
|
||||
|
||||
if ( wpf.isNumber( valueWithoutComma ) && value !== parseFloat( value ).toString() ) {
|
||||
$field.val( parseFloat( valueWithoutComma ) ).trigger( 'input' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if the default value is below the current min value.
|
||||
*
|
||||
* @since 1.9.4
|
||||
*
|
||||
* @param {jQuery} $defaultValueField jQuery object for the default value input.
|
||||
* @param {jQuery} $minField jQuery object for the min input field.
|
||||
*
|
||||
* @return {boolean} True if default value is less than min, otherwise false.
|
||||
*/
|
||||
isNeedAdjustDefaultValueByMinValue( $defaultValueField, $minField ) {
|
||||
const defaultValue = app.parseFieldValue( $defaultValueField ),
|
||||
min = app.parseFieldValue( $minField );
|
||||
|
||||
return wpf.isNumber( defaultValue ) && min !== null && defaultValue < min;
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if the default value is above the current max value.
|
||||
*
|
||||
* @since 1.9.4
|
||||
*
|
||||
* @param {jQuery} $defaultValueField jQuery object for the default value input.
|
||||
* @param {jQuery} $maxField jQuery object for the max input field.
|
||||
*
|
||||
* @return {boolean} True if default value is greater than max, otherwise false.
|
||||
*/
|
||||
isNeedAdjustDefaultValueByMaxValue( $defaultValueField, $maxField ) {
|
||||
const defaultValue = app.parseFieldValue( $defaultValueField ),
|
||||
max = app.parseFieldValue( $maxField );
|
||||
|
||||
return wpf.isNumber( defaultValue ) && max !== null && defaultValue > max;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the 'change' event for the default value field, keeping it in range.
|
||||
*
|
||||
* @since 1.9.4
|
||||
*
|
||||
* @param {Event} event The change event object.
|
||||
*/
|
||||
onChangeNumbersDefaultValue( event ) {
|
||||
if (
|
||||
app.tagClicked || // Tag was recently clicked to prevent unnecessary updates.
|
||||
event.handleObj?.type === 'focusout' // Event was triggered when editable tag was changed.
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const $defaultValueField = $( event.target );
|
||||
const $container = $defaultValueField.closest( '.wpforms-field-option-group' );
|
||||
const $minField = $container.find( '.wpforms-numbers-min' );
|
||||
const $maxField = $container.find( '.wpforms-numbers-max' );
|
||||
|
||||
app.normalizeFloatValue( $defaultValueField );
|
||||
|
||||
if ( app.isNeedAdjustDefaultValueByMinValue( $defaultValueField, $minField ) ) {
|
||||
app.adjustValue( $minField, $defaultValueField );
|
||||
}
|
||||
|
||||
if ( app.isNeedAdjustDefaultValueByMaxValue( $defaultValueField, $maxField ) ) {
|
||||
app.adjustValue( $maxField, $defaultValueField );
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
return app;
|
||||
}( document, window, jQuery ) );
|
||||
|
||||
WPForms.Admin.Builder.FieldNumbers.init();
|
||||
+1
@@ -0,0 +1 @@
|
||||
var WPForms=window.WPForms||{};WPForms.Admin=WPForms.Admin||{},WPForms.Admin.Builder=WPForms.Admin.Builder||{},WPForms.Admin.Builder.FieldNumbers=WPForms.Admin.Builder.FieldNumbers||(u=>{let i={$builder:null,tagClicked:!1,init(){u(i.ready)},ready(){i.$builder=u("#wpforms-builder"),i.numbersEvents()},numbersEvents(){i.$builder.on("change",".wpforms-field-option-number .wpforms-numbers-min",i.onChangeNumbersMin),i.$builder.on("change",".wpforms-field-option-number .wpforms-numbers-max",i.onChangeNumbersMax),i.$builder.on("input",".wpforms-field-option-number .wpforms-field-option-row-default_value .wpforms-smart-tags-widget-original",_.debounce(i.onChangeNumbersDefaultValue,500)),i.$builder.on("click",".wpforms-smart-tags-widget .tag",i.smartTagClickTracking)},smartTagClickTracking(){i.tagClicked=!0,setTimeout(()=>{i.tagClicked=!1},200)},parseFieldValue(e){return!e.length||""===e.val()||(e=parseFloat(e.val()),isNaN(e))?null:e},isInvalidMinMaxRange(e,a){e=i.parseFieldValue(e),a=i.parseFieldValue(a);return null!==e&&null!==a&&a<e},syncNumberMinAttribute(e,a){a.attr("min",i.parseFieldValue(e))},syncNumberMaxAttribute(e,a){e.attr("max",i.parseFieldValue(a))},adjustValue(e,a){a.val(i.parseFieldValue(e)).trigger("input").trigger("wpformsSmartTagsInputSync")},onChangeNumbersMin(e){var e=u(e.target),a=e.closest(".wpforms-field-option-group"),r=a.find(".wpforms-numbers-max"),a=a.find(".wpforms-field-option-row-default_value input.wpforms-smart-tags-widget-original");i.isInvalidMinMaxRange(e,r)&&i.adjustValue(r,e),i.isNeedAdjustDefaultValueByMinValue(a,e)&&i.adjustValue(e,a),i.syncNumberMinAttribute(e,r)},onChangeNumbersMax(e){var e=u(e.target),a=e.closest(".wpforms-field-option-group"),r=a.find(".wpforms-numbers-min"),a=a.find(".wpforms-field-option-row-default_value input.wpforms-smart-tags-widget-original");i.isInvalidMinMaxRange(r,e)&&i.adjustValue(r,e),i.isNeedAdjustDefaultValueByMaxValue(a,e)&&i.adjustValue(e,a),i.syncNumberMaxAttribute(r,e)},normalizeFloatValue(e){var a=e.val(),r=a.replace(",",".");wpf.isNumber(r)&&a!==parseFloat(a).toString()&&e.val(parseFloat(r)).trigger("input")},isNeedAdjustDefaultValueByMinValue(e,a){e=i.parseFieldValue(e),a=i.parseFieldValue(a);return wpf.isNumber(e)&&null!==a&&e<a},isNeedAdjustDefaultValueByMaxValue(e,a){e=i.parseFieldValue(e),a=i.parseFieldValue(a);return wpf.isNumber(e)&&null!==a&&a<e},onChangeNumbersDefaultValue(e){var a,r;i.tagClicked||"focusout"===e.handleObj?.type||(a=(r=(e=u(e.target)).closest(".wpforms-field-option-group")).find(".wpforms-numbers-min"),r=r.find(".wpforms-numbers-max"),i.normalizeFloatValue(e),i.isNeedAdjustDefaultValueByMinValue(e,a)&&i.adjustValue(a,e),i.isNeedAdjustDefaultValueByMaxValue(e,r)&&i.adjustValue(r,e))}};return i})((document,window,jQuery)),WPForms.Admin.Builder.FieldNumbers.init();
|
||||
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* WPForms Rating Field Builder Script
|
||||
*
|
||||
* @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.FieldRating = WPForms.Admin.Builder.FieldRating || ( function( document, window, $ ) { // eslint-disable-line
|
||||
|
||||
const app = {
|
||||
|
||||
/**
|
||||
* Initialize the application.
|
||||
*/
|
||||
init() {
|
||||
$( app.ready );
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the DOM is fully loaded.
|
||||
*/
|
||||
ready() {
|
||||
$( document )
|
||||
.on( 'input', '.wpforms-field-option-row-lowest_label input', app.updateLowestLabel )
|
||||
.on( 'input', '.wpforms-field-option-row-highest_label input', app.updateHighestLabel )
|
||||
.on( 'change', '.wpforms-field-option-row-label_position select', app.updateLabelPosition );
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the lowest label in the preview when the input changes.
|
||||
*
|
||||
* @since 1.9.8
|
||||
*
|
||||
* @param {Event} event The input event.
|
||||
*/
|
||||
updateLowestLabel( event ) {
|
||||
app.updateLabel( event, 'lowest' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the highest label in the preview when the input changes.
|
||||
*
|
||||
* @since 1.9.8
|
||||
*
|
||||
* @param {Event} event The input event.
|
||||
*/
|
||||
updateHighestLabel( event ) {
|
||||
app.updateLabel( event, 'highest' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the label in the preview based on the input value.
|
||||
*
|
||||
* @since 1.9.8
|
||||
*
|
||||
* @param {Event} event The input event.
|
||||
* @param {string} type The type of label being updated ('lowest' or 'highest').
|
||||
*/
|
||||
updateLabel( event, type ) {
|
||||
const $input = $( event.target ),
|
||||
label = $input.val(),
|
||||
$inputContainer = $input.closest( `.wpforms-field-option-row-${ type }_label` ),
|
||||
fieldId = $inputContainer.data( 'field-id' ),
|
||||
$previewField = $( `#wpforms-field-${ fieldId }` ),
|
||||
$previewLabel = $previewField.find( `.wpforms-rating-field-${ type }-label` );
|
||||
|
||||
// Update the label in the preview.
|
||||
$previewLabel.text( label );
|
||||
|
||||
// Show or hide the labels container based on whether any labels are set.
|
||||
app.toggleLabelsVisibility( $previewField );
|
||||
},
|
||||
|
||||
/**
|
||||
* Show or hide the labels container based on whether any labels are set.
|
||||
*
|
||||
* @since 1.9.8
|
||||
*
|
||||
* @param {jQuery} $previewField The jQuery object representing the preview field.
|
||||
*/
|
||||
toggleLabelsVisibility( $previewField ) {
|
||||
const labelsContainer = $previewField.find( '.wpforms-rating-field-labels' ),
|
||||
labels = labelsContainer.find( '.wpforms-sub-label' );
|
||||
|
||||
const labelsArray = labels.map( ( _, el ) => $( el ).text() ).get(),
|
||||
filteredLabels = labelsArray.filter( ( ratingLabel ) => ratingLabel.trim() !== '' );
|
||||
|
||||
labelsContainer.toggleClass( 'wpforms-hidden', filteredLabels.length === 0 );
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the label position in the preview when the select changes.
|
||||
*
|
||||
* @since 1.9.8
|
||||
*
|
||||
* @param {Event} event The change event.
|
||||
*/
|
||||
updateLabelPosition( event ) {
|
||||
const $select = $( event.target ),
|
||||
labelPosition = $select.val(),
|
||||
$inputContainer = $select.closest( '.wpforms-field-option-row-label_position' ),
|
||||
fieldId = $inputContainer.data( 'field-id' ),
|
||||
$previewField = $( `#wpforms-field-${ fieldId }` );
|
||||
|
||||
// Remove existing label position classes.
|
||||
$previewField.find( '.wpforms-rating-field-labels' ).toggleClass( 'wpforms-rating-field-labels-position-above', labelPosition === 'above' );
|
||||
},
|
||||
};
|
||||
|
||||
return app;
|
||||
}( document, window, jQuery ) ); // eslint-disable-line no-undef
|
||||
|
||||
// Initialize the application.
|
||||
WPForms.Admin.Builder.FieldRating.init();
|
||||
+1
@@ -0,0 +1 @@
|
||||
var WPForms=window.WPForms||{};WPForms.Admin=WPForms.Admin||{},WPForms.Admin.Builder=WPForms.Admin.Builder||{},WPForms.Admin.Builder.FieldRating=WPForms.Admin.Builder.FieldRating||((e,t)=>{let o={init(){t(o.ready)},ready(){t(e).on("input",".wpforms-field-option-row-lowest_label input",o.updateLowestLabel).on("input",".wpforms-field-option-row-highest_label input",o.updateHighestLabel).on("change",".wpforms-field-option-row-label_position select",o.updateLabelPosition)},updateLowestLabel(e){o.updateLabel(e,"lowest")},updateHighestLabel(e){o.updateLabel(e,"highest")},updateLabel(e,i){var e=t(e.target),l=e.val(),e=e.closest(`.wpforms-field-option-row-${i}_label`).data("field-id"),e=t("#wpforms-field-"+e);e.find(`.wpforms-rating-field-${i}-label`).text(l),o.toggleLabelsVisibility(e)},toggleLabelsVisibility(e){var e=e.find(".wpforms-rating-field-labels"),i=e.find(".wpforms-sub-label").map((e,i)=>t(i).text()).get().filter(e=>""!==e.trim());e.toggleClass("wpforms-hidden",0===i.length)},updateLabelPosition(e){var e=t(e.target),i=e.val(),e=e.closest(".wpforms-field-option-row-label_position").data("field-id");t("#wpforms-field-"+e).find(".wpforms-rating-field-labels").toggleClass("wpforms-rating-field-labels-position-above","above"===i)}};return o})(document,(window,jQuery)),WPForms.Admin.Builder.FieldRating.init();
|
||||
Reference in New Issue
Block a user