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:
root
2026-04-29 15:32:23 +00:00
parent 57b752f54e
commit b6df4dbb92
5385 changed files with 838580 additions and 2416 deletions
@@ -0,0 +1,125 @@
/* global wpforms_settings */
/**
* @param wpforms_settings.address_field.list_countries_without_states
*/
( function( window, $ ) {
const app = {
/**
* List of countries without states.
*
* @see WPForms\Forms\Fields\Address\Frontend::strings for PHP filter.
*
* @since 1.9.5
*/
noStateCountries: [],
/**
* Init Address field.
*
* @since 1.9.5
*/
init() {
$( window ).on( 'load', app.onLoad );
$( document )
.on( 'wpformsRepeaterFieldCloneCreated', app.setChangeHandlers );
},
/**
* On load event.
*
* @since 1.9.5
*/
onLoad() {
app.noStateCountries = wpforms_settings?.address_field?.list_countries_without_states || [];
if ( ! app.noStateCountries.length ) {
return;
}
app.setChangeHandlers();
},
/**
* Set change handlers.
*
* @since 1.9.5
*/
setChangeHandlers() {
$( '.wpforms-field-address' ).each( function() {
const $countrySelect = $( this ).find( 'select.wpforms-field-address-country' );
if ( ! $countrySelect.length ) {
return;
}
app.handleCountryChange( $countrySelect );
$countrySelect
.off( 'change' )
.on( 'change', function() {
app.handleCountryChange( this );
} );
} );
},
/**
* Handle country change.
*
* @since 1.9.5
*
* @param {HTMLElement} field Country select field.
*/
handleCountryChange( field ) {
const $this = $( field ),
$stateInput = $this.closest( '.wpforms-field' ).find( '.wpforms-field-address-state' ),
$rowWithState = $stateInput.closest( '.wpforms-field-row' );
if ( ! $rowWithState.length ) {
return;
}
const value = $this.val();
app.handleStateInput( $stateInput, $rowWithState, value );
},
/**
* Handle state input.
*
* @since 1.9.5
*
* @param {jQuery} $stateInput State input.
* @param {jQuery} $rowWithState Row with state.
* @param {string} countryValue Country value.
*/
handleStateInput( $stateInput, $rowWithState, countryValue ) {
if ( app.noStateCountries.includes( countryValue ) ) {
$stateInput
.val( '' )
.prop( 'disabled', true )
.prop( 'required', false )
.on( 'change', function() {
$( this ).val( '' );
} );
$rowWithState.addClass( 'wpforms-without-state' );
return;
}
$stateInput
.prop( 'disabled', false )
.prop( 'required', $rowWithState.find( '.wpforms-first input' ).prop( 'required' ) ) // Set required same as first input.
.off( 'change' );
$rowWithState.removeClass( 'wpforms-without-state' );
},
};
app.init();
return app;
}( window, jQuery ) );
@@ -0,0 +1 @@
((e,s)=>{let o={noStateCountries:[],init(){s(e).on("load",o.onLoad),s(document).on("wpformsRepeaterFieldCloneCreated",o.setChangeHandlers)},onLoad(){o.noStateCountries=wpforms_settings?.address_field?.list_countries_without_states||[],o.noStateCountries.length&&o.setChangeHandlers()},setChangeHandlers(){s(".wpforms-field-address").each(function(){var e=s(this).find("select.wpforms-field-address-country");e.length&&(o.handleCountryChange(e),e.off("change").on("change",function(){o.handleCountryChange(this)}))})},handleCountryChange(e){var e=s(e),t=e.closest(".wpforms-field").find(".wpforms-field-address-state"),n=t.closest(".wpforms-field-row");n.length&&(e=e.val(),o.handleStateInput(t,n,e))},handleStateInput(e,t,n){o.noStateCountries.includes(n)?(e.val("").prop("disabled",!0).prop("required",!1).on("change",function(){s(this).val("")}),t.addClass("wpforms-without-state")):(e.prop("disabled",!1).prop("required",t.find(".wpforms-first input").prop("required")).off("change"),t.removeClass("wpforms-without-state"))}};o.init(),o})(window,jQuery);
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
!function i(r,a,o){function l(n,t){if(!a[n]){if(!r[n]){var e="function"==typeof require&&require;if(!t&&e)return e(n,!0);if(s)return s(n,!0);throw new Error("Cannot find module '"+n+"'")}t=a[n]={exports:{}};r[n][0].call(t.exports,function(t){var e=r[n][1][t];return l(e||t)},t,t.exports,i,r,a,o)}return a[n].exports}for(var s="function"==typeof require&&require,t=0;t<o.length;t++)l(o[t]);return l}({1:[function(t,e,n){function r(t){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function l(t,e,n){return t.replace("{count}",e).replace("{limit}",n).replace("{remaining}",n-e)}function s(t,e,n){var i=document.createElement("div");return t="object"===r(t)?"":t,e="object"===r(e)?"":e,i.classList.add("wpforms-field-limit-text"),i.id="wpforms-field-limit-text-"+t+"-"+e,i.setAttribute("aria-live","polite"),i.textContent=n,i}function u(e){return"string"==typeof e&&e.length?([/([A-Z]+),([A-Z]+)/gi,/([0-9]+),([A-Z]+)/gi,/([A-Z]+),([0-9]+)/gi].forEach(function(t){e=e.replace(t,"$1, $2")}),e.split(/\s+/).length):0}function c(t){return window.clipboardData&&window.clipboardData.getData?window.clipboardData.getData("Text"):t.clipboardData&&t.clipboardData.getData?t.clipboardData.getData("text/plain"):""}function d(t,e){var n="",i=/\s+/g,r=t.trim().match(i)||[],a=t.split(i);a.splice(e,a.length);for(var o=0;o<a.length;o++)n+=a[o]+(r[o]||"");return n.trim()}function i(t){return[].slice.call(t)}function f(t){(t=t.parentNode.querySelector(".wpforms-field-limit-text"))&&t.remove()}function a(){(window.WPFormsTextLimit=o).initHint("body")}var o;o={initHint:function(t){i(document.querySelectorAll(t+" .wpforms-limit-characters-enabled")).map(function(t){function e(t){n.textContent=l(window.wpforms_settings.val_limit_characters,this.value.length,i)}var n,i,r,a=parseInt(t.dataset.textLimit,10)||0,o=(t.value=t.value.slice(0,a),s(t.dataset.formId,t.dataset.fieldId,l(wpforms_settings.val_limit_characters,t.value.length,a)));n=o,i=a;f(t),t.parentNode.appendChild(o),t.addEventListener("keydown",e),t.addEventListener("keyup",e),t.addEventListener("paste",(r=a,function(t){t.preventDefault();var t=c(t),e=this.selectionStart+t.length,t=this.value.substring(0,this.selectionStart)+t+this.value.substring(this.selectionStart);this.value=t.substring(0,r),this.setSelectionRange(e,e)}))}),i(document.querySelectorAll(t+" .wpforms-limit-words-enabled")).map(function(t){function e(t){var e=u(this.value.trim());n.textContent=l(window.wpforms_settings.val_limit_words,e,i),-1<[13,32,188].indexOf(t.keyCode)&&i<=e&&t.preventDefault()}var n,i,r,a=parseInt(t.dataset.textLimit,10)||0,o=(t.value=d(t.value,a),s(t.dataset.formId,t.dataset.fieldId,l(wpforms_settings.val_limit_words,u(t.value.trim()),a)));n=o,i=a;f(t),t.parentNode.appendChild(o),t.addEventListener("keydown",e),t.addEventListener("keyup",e),t.addEventListener("paste",(r=a,function(t){t.preventDefault();var t=c(t),e=this.selectionStart+t.length,t=this.value.substring(0,this.selectionStart)+t+this.value.substring(this.selectionStart);this.value=d(t,r),this.setSelectionRange(e,e)}))})}},"loading"===document.readyState?document.addEventListener("DOMContentLoaded",a):a()},{}]},{},[1]);
@@ -0,0 +1,338 @@
/* global wpforms_settings */
( function() {
/**
* Predefine hint text to display.
*
* @since 1.5.6
* @since 1.6.4 Added a new macros - {remaining}.
*
* @param {string} hintText Hint text.
* @param {number} count Current count.
* @param {number} limit Limit to.
*
* @return {string} Predefined hint text.
*/
function renderHint( hintText, count, limit ) {
return hintText.replace( '{count}', count ).replace( '{limit}', limit ).replace( '{remaining}', limit - count );
}
/**
* Create HTMLElement hint element with text.
*
* @since 1.5.6
*
* @param {number|string} formId Form id.
* @param {number|string} fieldId Form field id.
* @param {string} text Hint text.
*
* @return {Object} HTMLElement hint element with text.
*/
function createHint( formId, fieldId, text ) {
const hint = document.createElement( 'div' );
formId = typeof formId === 'object' ? '' : formId;
fieldId = typeof fieldId === 'object' ? '' : fieldId;
hint.classList.add( 'wpforms-field-limit-text' );
hint.id = 'wpforms-field-limit-text-' + formId + '-' + fieldId;
hint.setAttribute( 'aria-live', 'polite' );
hint.textContent = text;
return hint;
}
/**
* Keyup/Keydown event higher order function for characters limit.
*
* @since 1.5.6
*
* @param {Object} hint HTMLElement hint element.
* @param {number} limit Max allowed number of characters.
*
* @return {Function} Handler function.
*/
function checkCharacters( hint, limit ) {
// noinspection JSUnusedLocalSymbols
return function( e ) { // eslint-disable-line no-unused-vars
hint.textContent = renderHint(
window.wpforms_settings.val_limit_characters,
this.value.length,
limit
);
};
}
/**
* Count words in the string.
*
* @since 1.6.2
*
* @param {string} string String value.
*
* @return {number} Words count.
*/
function countWords( string ) {
if ( typeof string !== 'string' ) {
return 0;
}
if ( ! string.length ) {
return 0;
}
[
/([A-Z]+),([A-Z]+)/gi,
/([0-9]+),([A-Z]+)/gi,
/([A-Z]+),([0-9]+)/gi,
].forEach( function( pattern ) {
string = string.replace( pattern, '$1, $2' );
} );
return string.split( /\s+/ ).length;
}
/**
* Keyup/Keydown event higher order function for words limit.
*
* @since 1.5.6
*
* @param {Object} hint HTMLElement hint element.
* @param {number} limit Max allowed number of characters.
*
* @return {Function} Handler function.
*/
function checkWords( hint, limit ) {
return function( e ) {
const value = this.value.trim(),
words = countWords( value );
hint.textContent = renderHint(
window.wpforms_settings.val_limit_words,
words,
limit
);
// We should prevent the keys: Enter, Space, Comma.
if ( [ 13, 32, 188 ].indexOf( e.keyCode ) > -1 && words >= limit ) {
e.preventDefault();
}
};
}
/**
* Get passed text from the clipboard.
*
* @since 1.5.6
*
* @param {ClipboardEvent} e Clipboard event.
*
* @return {string} Text from clipboard.
*/
function getPastedText( e ) {
if ( window.clipboardData && window.clipboardData.getData ) { // IE
return window.clipboardData.getData( 'Text' );
} else if ( e.clipboardData && e.clipboardData.getData ) {
return e.clipboardData.getData( 'text/plain' );
}
return '';
}
/**
* Paste event higher order function for character limit.
*
* @since 1.6.7.1
*
* @param {number} limit Max allowed number of characters.
*
* @return {Function} Event handler.
*/
function pasteText( limit ) {
return function( e ) {
e.preventDefault();
const pastedText = getPastedText( e ),
newPosition = this.selectionStart + pastedText.length,
newText = this.value.substring( 0, this.selectionStart ) + pastedText + this.value.substring( this.selectionStart );
this.value = newText.substring( 0, limit );
this.setSelectionRange( newPosition, newPosition );
};
}
/**
* Limit string length to a certain number of words, preserving line breaks.
*
* @since 1.6.8
*
* @param {string} text Text.
* @param {number} limit Max allowed number of words.
*
* @return {string} Text with the limited number of words.
*/
function limitWords( text, limit ) {
let result = '';
// Regular expression pattern: match any space character.
const regEx = /\s+/g;
// Store separators for further join.
const separators = text.trim().match( regEx ) || [];
// Split the new text by regular expression.
const newTextArray = text.split( regEx );
// Limit the number of words.
newTextArray.splice( limit, newTextArray.length );
// Join the words together using stored separators.
for ( let i = 0; i < newTextArray.length; i++ ) {
result += newTextArray[ i ] + ( separators[ i ] || '' );
}
return result.trim();
}
/**
* Paste event higher order function for words limit.
*
* @since 1.5.6
*
* @param {number} limit Max allowed number of words.
*
* @return {Function} Event handler.
*/
function pasteWords( limit ) {
return function( e ) {
e.preventDefault();
const pastedText = getPastedText( e ),
newPosition = this.selectionStart + pastedText.length,
newText = this.value.substring( 0, this.selectionStart ) + pastedText + this.value.substring( this.selectionStart );
this.value = limitWords( newText, limit );
this.setSelectionRange( newPosition, newPosition );
};
}
/**
* Array.from polyfill.
*
* @since 1.5.6
*
* @param {Object} el Iterator.
*
* @return {Object} Array.
*/
function arrFrom( el ) {
return [].slice.call( el );
}
/**
* Remove existing hint.
*
* @since 1.9.5.1
*
* @param {Object} element Element.
*/
const removeExistingHint = ( element ) => {
const existingHint = element.parentNode.querySelector( '.wpforms-field-limit-text' );
if ( existingHint ) {
existingHint.remove();
}
};
/**
* Public functions and properties.
*
* @since 1.8.9
*
* @type {Object}
*/
const app = {
/**
* Init text limit hint.
*
* @since 1.8.9
*
* @param {string} context Context selector.
*/
initHint( context ) {
arrFrom( document.querySelectorAll( context + ' .wpforms-limit-characters-enabled' ) )
.map(
function( e ) { // eslint-disable-line array-callback-return
const limit = parseInt( e.dataset.textLimit, 10 ) || 0;
e.value = e.value.slice( 0, limit );
const hint = createHint(
e.dataset.formId,
e.dataset.fieldId,
renderHint(
wpforms_settings.val_limit_characters,
e.value.length,
limit
)
);
const fn = checkCharacters( hint, limit );
removeExistingHint( e );
e.parentNode.appendChild( hint );
e.addEventListener( 'keydown', fn );
e.addEventListener( 'keyup', fn );
e.addEventListener( 'paste', pasteText( limit ) );
}
);
arrFrom( document.querySelectorAll( context + ' .wpforms-limit-words-enabled' ) )
.map(
function( e ) { // eslint-disable-line array-callback-return
const limit = parseInt( e.dataset.textLimit, 10 ) || 0;
e.value = limitWords( e.value, limit );
const hint = createHint(
e.dataset.formId,
e.dataset.fieldId,
renderHint(
wpforms_settings.val_limit_words,
countWords( e.value.trim() ),
limit
)
);
const fn = checkWords( hint, limit );
removeExistingHint( e );
e.parentNode.appendChild( hint );
e.addEventListener( 'keydown', fn );
e.addEventListener( 'keyup', fn );
e.addEventListener( 'paste', pasteWords( limit ) );
}
);
},
};
/**
* DOMContentLoaded handler.
*
* @since 1.5.6
*/
function ready() {
// Expose to the world.
window.WPFormsTextLimit = app;
app.initHint( 'body' );
}
if ( document.readyState === 'loading' ) {
document.addEventListener( 'DOMContentLoaded', ready );
} else {
ready();
}
}() );
@@ -0,0 +1,21 @@
// Clear URL - remove wpforms_form_id
( function() {
var loc = window.location,
query = loc.search;
if ( query.indexOf( 'wpforms_form_id=' ) !== -1 ) {
query = query.replace( /([&?]wpforms_form_id=[0-9]*$|wpforms_form_id=[0-9]*&|[?&]wpforms_form_id=[0-9]*(?=#))/, '' );
history.replaceState( {}, null, loc.origin + loc.pathname + query );
}
}() );
( function( $ ) {
$( function() {
if ( $( 'div.wpforms-confirmation-scroll' ).length ) {
$( 'html,body' ).animate(
{ scrollTop: ( $( 'div.wpforms-confirmation-scroll' ).offset().top ) - 100 },
1000
);
}
} );
}( jQuery ) );
@@ -0,0 +1 @@
(()=>{var o=window.location,r=o.search;-1!==r.indexOf("wpforms_form_id=")&&(r=r.replace(/([&?]wpforms_form_id=[0-9]*$|wpforms_form_id=[0-9]*&|[?&]wpforms_form_id=[0-9]*(?=#))/,""),history.replaceState({},null,o.origin+o.pathname+r))})(),(o=>{o(function(){o("div.wpforms-confirmation-scroll").length&&o("html,body").animate({scrollTop:o("div.wpforms-confirmation-scroll").offset().top-100},1e3)})})(jQuery);
@@ -0,0 +1,544 @@
/* global wpforms_settings, WPFormsUtils */
/**
* @param wpforms_settings.css_vars
* @param wpforms_settings.formErrorMessagePrefix
* @param wpforms_settings.indicatorStepsPattern
* @param wpforms_settings.submitBtnDisabled
*/
// noinspection ES6ConvertVarToLetConst
/**
* Modern Frontend.
*
* @since 1.8.1
*/
// eslint-disable-next-line no-var
var WPForms = window.WPForms || {};
WPForms.FrontendModern = WPForms.FrontendModern || ( function( document, window, $ ) {
// noinspection JSUnusedLocalSymbols,JSUnusedGlobalSymbols
const app = {
/**
* Start the engine.
*
* @since 1.8.1
*/
init() {
// Document ready.
$( app.ready );
app.bindOptinMonster();
},
/**
* Document ready.
*
* @since 1.8.1
*/
ready() {
app.updateGBBlockAccentColors();
app.initPageBreakButtons();
app.initButtonStyle();
app.events();
},
/**
* Events.
*
* @since 1.8.1
*/
events() {
$( document )
.on( 'wpforms_elementor_form_fields_initialized', app.initPageBreakButtons );
$( 'form.wpforms-form' )
.on( 'wpformsCombinedUploadsSizeError', app.combinedUploadsSizeError )
.on( 'wpformsFormSubmitButtonDisable', app.formSubmitButtonDisable )
.on( 'wpformsFormSubmitButtonRestore', app.formSubmitButtonRestore )
.on( 'wpformsPageChange', app.pageChange );
$( 'form.wpforms-form .wpforms-submit' )
.on( 'keydown click', app.disabledButtonPress );
// Add styling to timepicker dropdown.
$( document )
.on( 'focus', '.wpforms-render-modern .wpforms-timepicker', app.updateTimepickerDropdown );
// Reset timepicker dropdown styles.
$( document )
.on( 'focusout', '.wpforms-render-modern .wpforms-timepicker', app.resetTimepickerDropdown );
},
/**
* OptinMonster compatibility.
*
* Re-initialize after OptinMonster loads to accommodate changes that
* have occurred to the DOM.
*
* @since 1.9.0
*/
bindOptinMonster() {
// OM v5.
document.addEventListener( 'om.Campaign.load', function() {
app.ready();
} );
// OM Legacy.
$( document ).on( 'OptinMonsterOnShow', function() {
app.ready();
} );
},
/**
* Add styling to timepicker dropdown.
*
* @since 1.8.8
*/
updateTimepickerDropdown() {
const cssVars = app.getCssVars( $( this ) );
setTimeout(
function() {
const $list = $( '.ui-timepicker-wrapper .ui-timepicker-list' );
$list.css( 'background', cssVars[ 'field-menu-color' ] );
$list.find( 'li' ).css( 'color', cssVars[ 'field-text-color' ] );
$list.find( '.ui-timepicker-selected' )
.css( 'background', cssVars[ 'button-background-color' ] )
.css( 'color', cssVars[ 'button-text-color' ] );
},
0
);
},
/**
* Reset timepicker dropdown styles.
*
* @since 1.8.9.5
*/
resetTimepickerDropdown() {
setTimeout(
function() {
const $list = $( '.ui-timepicker-wrapper .ui-timepicker-list' );
$list.find( ':not(.ui-timepicker-selected)' ).attr( 'style', '' );
},
0
);
},
/**
* Update accent colors of some fields in GB block in Modern Markup mode.
*
* @since 1.8.8
*/
initButtonStyle() {
// Loop through all the GB blocks on the page.
$( '.wpforms-block.wpforms-container-full, .elementor-widget-wpforms .wpforms-container-full' ).each( function() {
const $form = $( this );
const contStyle = getComputedStyle( $form.get( 0 ) );
const btnBgColor = app.getCssVar( contStyle, '--wpforms-button-background-color-alt' );
if ( app.isTransparentColor( btnBgColor ) ) {
$form.find( 'button.wpforms-submit' ).addClass( 'wpforms-opacity-hover' );
}
} );
},
/**
* Checks if the provided color has transparency.
*
* @since 1.8.8
*
* @param {string} color The color to check.
*
* @return {boolean} Returns true if the color is transparent.
*/
isTransparentColor( color ) {
const rgba = app.getColorAsRGBArray( color );
// The max opacity value of the color that is considered as transparent.
const opacityThreshold = 0.33;
const opacity = Number( rgba?.[ 3 ] );
// Compare the opacity value with the threshold.
return opacity <= opacityThreshold;
},
/**
* Update accent colors of some fields in GB block in Modern Markup mode.
*
* @since 1.8.1
*/
updateGBBlockAccentColors() {
// Loop through all the GB blocks on the page.
$( '.wpforms-block.wpforms-container-full, .elementor-widget-wpforms .wpforms-container-full' ).each( function() {
const $form = $( this );
app.updateGBBlockPageIndicatorColor( $form );
app.updateGBBlockIconChoicesColor( $form );
app.updateGBBlockRatingColor( $form );
} );
},
/**
* Update accent color of Page Indicator.
*
* @since 1.8.1
*
* @param {jQuery} $form Form container.
*/
updateGBBlockPageIndicatorColor( $form ) {
const $indicator = $form.find( '.wpforms-page-indicator' ),
$indicatorPage = $indicator.find( '.wpforms-page-indicator-page-progress, .wpforms-page-indicator-page.active .wpforms-page-indicator-page-number' ),
$indicatorTriangle = $indicatorPage.find( '.wpforms-page-indicator-page-triangle' );
$indicator.data( 'indicator-color', 'var( --wpforms-page-break-color )' );
$indicatorPage.css( 'background-color', 'var( --wpforms-page-break-color )' );
$indicatorTriangle.css( 'border-top-color', 'var( --wpforms-page-break-color )' );
},
/**
* Update accent color of Icon Choices.
*
* @since 1.8.1
*
* @param {jQuery} $form Form container.
*/
updateGBBlockIconChoicesColor( $form ) {
$form
.find( '.wpforms-icon-choices' )
.css( '--wpforms-icon-choices-color', 'var( --wpforms-button-background-color )' );
},
/**
* Update accent color of Rating field.
*
* @since 1.8.1
*
* @param {jQuery} $form Form container.
*/
updateGBBlockRatingColor( $form ) {
$form
.find( '.wpforms-field-rating-item svg' )
.css( 'color', 'var( --wpforms-page-break-color, var( --wpforms-button-background-color ) )' );
},
/**
* Init Page Break fields.
*
* @since 1.8.1
*/
initPageBreakButtons() {
$( '.wpforms-page-button' )
.removeClass( 'wpforms-disabled' )
.attr( 'aria-disabled', 'false' )
.attr( 'aria-describedby', '' );
},
/**
* Handler for `wpformsCombinedUploadsSizeError` event.
* Accessibility enhancements to error container and submit button.
*
* @since 1.8.1
*
* @param {Object} e Event object.
* @param {jQuery} $form Form object.
* @param {jQuery} $errorCnt Error container object.
*/
combinedUploadsSizeError( e, $form, $errorCnt ) {
const formId = $form.data( 'formid' ),
errormessage = $form.attr( 'aria-errormessage' ) || '',
errorCntId = `wpforms-${ formId }-footer-error`,
$submitBtn = $form.find( '.wpforms-submit' );
$form.attr( {
'aria-invalid': 'true',
'aria-errormessage': `${ errormessage } ${ errorCntId }`,
} );
$errorCnt.attr( {
role: 'alert',
id: errorCntId,
} );
// Add error message prefix.
$errorCnt.find( '> .wpforms-hidden:first-child' ).remove();
$errorCnt.prepend( `<span class="wpforms-hidden">${ wpforms_settings.formErrorMessagePrefix }</span>` );
// Instead of set the `disabled` property,
// we must use `aria-disabled` and `aria-describedby` attributes in conduction with `wpforms-disabled` class.
$submitBtn.attr( 'aria-describedby', errorCntId );
},
/**
* Handler for `wpformsCombinedUploadsSizeOk` event.
*
* @since 1.8.1
* @deprecated 1.8.3
*
* @param {Object} e Event object.
* @param {jQuery} $form Form object.
* @param {jQuery} $errorCnt Error container object.
*/
// eslint-disable-next-line no-unused-vars
combinedUploadsSizeOk( e, $form, $errorCnt ) {
// eslint-disable-next-line no-console
console.warn( 'WARNING! Function "WPForms.FrontendModern( e, $form, $errorCnt )" has been deprecated, please use the new "formSubmitButtonDisable: function( e, $form, $submitBtn )" function instead!' );
const $submitBtn = $form.find( '.wpforms-submit' );
// Revert aria-* attributes to the normal state.
$submitBtn
.removeClass( 'wpforms-disabled' )
.attr( 'aria-disabled', 'false' )
.attr( 'aria-describedby', '' );
},
/**
* Handler for `wpformsFormSubmitButtonDisable` event.
*
* @since 1.8.1
*
* @param {Object} e Event object.
* @param {jQuery} $form Form object.
* @param {jQuery} $submitBtn Submit a button object.
*/
formSubmitButtonDisable( e, $form, $submitBtn ) {
const disabledBtnDescId = $form.attr( 'id' ) + '-submit-btn-disabled';
$submitBtn.before( `<div class="wpforms-hidden" id="${ disabledBtnDescId }">${ wpforms_settings.submitBtnDisabled }</div>` );
$submitBtn
.prop( 'disabled', false )
.addClass( 'wpforms-disabled' )
.attr( 'aria-disabled', 'true' )
.attr( 'aria-describedby', disabledBtnDescId );
},
/**
* Handler for `wpformsFormSubmitButtonRestore` event.
*
* @since 1.8.1
*
* @param {Object} e Event object.
* @param {jQuery} $form Form object.
* @param {jQuery} $submitBtn Submit a button object.
*/
formSubmitButtonRestore( e, $form, $submitBtn ) {
const disabledBtnDescId = $form.attr( 'id' ) + '-submit-btn-disabled';
$form.find( '#' + disabledBtnDescId ).remove();
$submitBtn
.removeClass( 'wpforms-disabled' )
.attr( 'aria-disabled', 'false' )
.attr( 'aria-describedby', '' );
},
/**
* Disabled button click/keydown event handler.
*
* @since 1.8.1
*
* @param {Object} e Event object.
*/
disabledButtonPress( e ) {
const $submitBtn = $( this );
if ( ! $submitBtn.hasClass( 'wpforms-disabled' ) ) {
return;
}
if ( e.key === 'Enter' || e.type === 'click' ) {
e.preventDefault();
e.stopImmediatePropagation();
}
},
/**
* Page change event handler.
*
* @since 1.8.1
*
* @param {Object} e Event object.
* @param {number} nextPage The next page number.
* @param {jQuery} $form Current form.
*/
pageChange( e, nextPage, $form ) {
const $pageIndicator = $form.find( '.wpforms-page-indicator' );
if ( ! wpforms_settings.indicatorStepsPattern || ! $pageIndicator.length ) {
return;
}
const totalPages = $form.find( '.wpforms-page' ).length;
let msg = wpforms_settings.indicatorStepsPattern;
let pageTitle;
msg = msg.replace( '{current}', nextPage ).replace( '{total}', totalPages );
if ( $pageIndicator.hasClass( 'progress' ) ) {
pageTitle = $pageIndicator.find( '.wpforms-page-indicator-page-title' ).data( `page-${ nextPage }-title` );
} else {
pageTitle = $pageIndicator.find( `.wpforms-page-indicator-page-${ nextPage } .wpforms-page-indicator-page-title` ).text();
}
msg = pageTitle ? pageTitle + '. ' + msg : msg;
$pageIndicator.attr( 'aria-valuenow', nextPage );
app.screenReaderAnnounce( msg, 'polite' );
},
/**
* Allows the screen reader to talk directly through the use of JS.
*
* @since 1.8.1
*
* @param {string} text The message to be vocalised
* @param {string} priority Aria-live priority. "polite" (by default) or "assertive".
*/
screenReaderAnnounce( text, priority ) {
const el = document.createElement( 'div' );
const id = 'wpforms-screen-reader-announce-' + Date.now();
el.setAttribute( 'id', id );
el.setAttribute( 'aria-live', priority || 'polite' );
el.classList.add( 'wpforms-screen-reader-announce' );
const node = document.body.appendChild( el );
setTimeout( function() {
node.innerHTML = text;
}, 100 );
setTimeout( function() {
document.body.removeChild( node );
}, 1000 );
},
/**
* Add opacity to color string.
* Supports formats: RGB, RGBA, HEX, HEXA.
*
* If the given color has an alpha channel, the new alpha channel will be calculated according to the given opacity.
*
* @since 1.8.1
*
* @param {string} color Color.
* @param {string} opacity Opacity.
*
* @return {string} Color in RGBA format with an added alpha channel according to given opacity.
*/
getColorWithOpacity( color, opacity ) {
// Moved to ../share/utils.js
return WPFormsUtils.cssColorsUtils.getColorWithOpacity( color, opacity );
},
/**
* Remove opacity from the color value.
* Supports formats: RGB, RGBA, HEX, HEXA.
*
* @since 1.8.1
*
* @param {string} color Color.
*
* @return {string} Color in RGB format.
*/
getSolidColor( color ) {
color = color.trim();
const rgbArray = app.getColorAsRGBArray( color );
if ( ! rgbArray ) {
return color;
}
// Combine and return the RGB color.
return `rgb(${ rgbArray[ 0 ] },${ rgbArray[ 1 ] },${ rgbArray[ 2 ] })`;
},
/**
* Check if the given color is a valid CSS color.
*
* @since 1.8.1
*
* @param {string} color Color.
*
* @return {boolean} True if the given color is a valid CSS color.
*/
isValidColor( color ) {
// Moved to ../share/utils.js
return WPFormsUtils.cssColorsUtils.isValidColor( color );
},
/**
* Get color as an array of RGB(A) values.
*
* @since 1.8.1
*
* @param {string} color Color.
*
* @return {Array|boolean} Color as an array of RGBA values. False on error.
*/
getColorAsRGBArray( color ) {
// Moved to ../share/utils.js
return WPFormsUtils.cssColorsUtils.getColorAsRGBArray( color );
},
/**
* Get CSS variable value.
*
* @since 1.8.1
*
* @param {Object} style Computed style object.
* @param {string} varName Style custom property name.
*
* @return {string|null} CSS variable value;
*/
getCssVar( style, varName ) {
if ( ! style || typeof style.getPropertyValue !== 'function' ) {
return null;
}
let value = style.getPropertyValue( varName ).trim();
if ( varName.includes( 'color' ) ) {
value = value.replace( /\s/g, '' );
}
return value;
},
/**
* Get all CSS variables.
*
* @since 1.8.1
*
* @param {jQuery} $form Form OR any element inside the form.
*
* @return {Object} CSS variables;
*/
getCssVars( $form ) {
if ( ! $form || ! $form.length ) {
return null;
}
const $cont = $form.hasClass( 'wpforms-container' ) ? $form : $form.closest( '.wpforms-container' );
const contStyle = getComputedStyle( $cont.get( 0 ) );
const cssVars = wpforms_settings.css_vars;
const vars = {};
for ( let i = 0; i < cssVars.length; i++ ) {
vars[ cssVars[ i ] ] = app.getCssVar( contStyle, '--wpforms-' + cssVars[ i ] );
}
return vars;
},
};
return app;
}( document, window, jQuery ) );
// Initialize.
WPForms.FrontendModern.init();
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long