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:
+170
@@ -0,0 +1,170 @@
|
||||
/* global wpforms_ai_chat_element */
|
||||
|
||||
/**
|
||||
* @param wpforms_ai_chat_element.ajaxurl
|
||||
* @param wpforms_ai_chat_element.errors.network
|
||||
* @param wpforms_ai_chat_element.errors.default
|
||||
*/
|
||||
|
||||
/**
|
||||
* The WPForms AI API wrapper.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*
|
||||
* @return {Function} The app cloning function.
|
||||
*/
|
||||
export default function() { // eslint-disable-line no-unused-vars, max-lines-per-function
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const app = {
|
||||
/**
|
||||
* AI chat mode.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
mode: '',
|
||||
|
||||
/**
|
||||
* AI AJAX actions.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
actions: {
|
||||
rate: 'wpforms_rate_ai_response',
|
||||
choices: 'wpforms_get_ai_choices',
|
||||
forms: 'wpforms_get_ai_form',
|
||||
},
|
||||
|
||||
/**
|
||||
* AJAX request.
|
||||
*
|
||||
* @param {Object} data Data to send.
|
||||
*
|
||||
* @return {Promise} The fetch result data promise.
|
||||
*/
|
||||
// eslint-disable-next-line complexity
|
||||
async ajax( data ) {
|
||||
if ( ! data.nonce ) {
|
||||
data.nonce = wpforms_ai_chat_element.nonce;
|
||||
}
|
||||
|
||||
const options = {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: new URLSearchParams( data ).toString(),
|
||||
};
|
||||
|
||||
const response = await fetch( wpforms_ai_chat_element.ajaxurl, options )
|
||||
.catch( ( error ) => {
|
||||
if ( error.message === 'Failed to fetch' ) {
|
||||
throw new Error( wpforms_ai_chat_element.errors.network );
|
||||
} else {
|
||||
throw new Error( error.message );
|
||||
}
|
||||
} );
|
||||
|
||||
if ( ! response.ok ) {
|
||||
throw new Error( wpforms_ai_chat_element.errors.network );
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if ( ! result.success || result.data?.error ) {
|
||||
throw new Error(
|
||||
result.data?.error ?? wpforms_ai_chat_element.errors.default,
|
||||
{
|
||||
cause: result.data?.code ?? 400,
|
||||
} );
|
||||
}
|
||||
|
||||
return result.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Prompt.
|
||||
*
|
||||
* @param {string} prompt The question to ask.
|
||||
* @param {string} sessionId Session ID.
|
||||
*
|
||||
* @return {Promise} The response data in promise.
|
||||
*/
|
||||
async prompt( prompt, sessionId ) {
|
||||
const data = {
|
||||
action: app.actions[ this.mode ] ?? app.actions.choices,
|
||||
prompt,
|
||||
};
|
||||
|
||||
if ( sessionId ) {
|
||||
data.session_id = sessionId; // eslint-disable-line camelcase
|
||||
}
|
||||
|
||||
return app.ajax( data );
|
||||
},
|
||||
|
||||
/**
|
||||
* Rate.
|
||||
*
|
||||
* @param {boolean} helpful Whether the response was helpful or not.
|
||||
* @param {string} responseId Response ID.
|
||||
*
|
||||
* @return {Promise} The response data in promise.
|
||||
*/
|
||||
async rate( helpful, responseId ) {
|
||||
const data = {
|
||||
action: app.actions.rate,
|
||||
helpful,
|
||||
response_id: responseId, // eslint-disable-line camelcase
|
||||
};
|
||||
|
||||
return app.ajax( data );
|
||||
},
|
||||
|
||||
setUp() {
|
||||
app.actions = {
|
||||
...app.actions,
|
||||
...wpforms_ai_chat_element.actions,
|
||||
};
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the AI chat mode.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*
|
||||
* @param {string} mode The mode to set.
|
||||
*
|
||||
* @return {Object} The app object.
|
||||
*/
|
||||
setMode( mode ) {
|
||||
this.mode = mode;
|
||||
|
||||
return this;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a clone of an app object.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*
|
||||
* @param {string} mode The AI prompt mode.
|
||||
*
|
||||
* @return {Object} Cloned app object.
|
||||
*/
|
||||
return function( mode ) {
|
||||
const obj = { ...app };
|
||||
|
||||
return obj.setUp().setMode( mode );
|
||||
};
|
||||
}
|
||||
Vendored
Executable
+1
@@ -0,0 +1 @@
|
||||
export default function(){let t={mode:"",actions:{rate:"wpforms_rate_ai_response",choices:"wpforms_get_ai_choices",forms:"wpforms_get_ai_form"},async ajax(e){e.nonce||(e.nonce=wpforms_ai_chat_element.nonce);e={method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams(e).toString()},e=await fetch(wpforms_ai_chat_element.ajaxurl,e).catch(e=>{throw"Failed to fetch"===e.message?new Error(wpforms_ai_chat_element.errors.network):new Error(e.message)});if(!e.ok)throw new Error(wpforms_ai_chat_element.errors.network);e=await e.json();if(!e.success||e.data?.error)throw new Error(e.data?.error??wpforms_ai_chat_element.errors.default,{cause:e.data?.code??400});return e.data},async prompt(e,r){e={action:t.actions[this.mode]??t.actions.choices,prompt:e};return r&&(e.session_id=r),t.ajax(e)},async rate(e,r){e={action:t.actions.rate,helpful:e,response_id:r};return t.ajax(e)},setUp(){return t.actions={...t.actions,...wpforms_ai_chat_element.actions},this},setMode(e){return this.mode=e,this}};return function(e){return{...t}.setUp().setMode(e)}}
|
||||
Executable
+319
@@ -0,0 +1,319 @@
|
||||
/* global WPFormsAIChatHTMLElement, WPFormsBuilder, wpf, wpforms_builder */
|
||||
|
||||
/**
|
||||
* The WPForms AI chat element.
|
||||
*
|
||||
* Choices helpers module.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*
|
||||
* @param {WPFormsAIChatHTMLElement} chat The chat element.
|
||||
*
|
||||
* @return {Object} The choices' helpers object.
|
||||
*/
|
||||
export default function( chat ) { // eslint-disable-line max-lines-per-function
|
||||
/**
|
||||
* The `choices` mode helpers object.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*/
|
||||
return {
|
||||
/**
|
||||
* Get the `choices` answer based on AI response data.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*
|
||||
* @param {Object} response The response data.
|
||||
*
|
||||
* @return {string} Answer HTML markup.
|
||||
*/
|
||||
getAnswer( response ) {
|
||||
if ( response.choices?.length < 1 ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const li = [];
|
||||
|
||||
for ( const i in response.choices ) {
|
||||
li.push( `
|
||||
<li class="wpforms-ai-chat-choices-item">
|
||||
${ chat.htmlSpecialChars( response.choices[ i ] ) }
|
||||
</li>
|
||||
` );
|
||||
}
|
||||
|
||||
let answerHtml = `
|
||||
<h4>${ chat.htmlSpecialChars( response.heading ?? '' ) }</h4>
|
||||
<ol>
|
||||
${ li.join( '' ) }
|
||||
</ol>
|
||||
`;
|
||||
|
||||
// Add footer to the first answer only.
|
||||
if ( ! chat.sessionId ) {
|
||||
answerHtml += `<span>${ chat.modeStrings.footer }</span>`;
|
||||
}
|
||||
|
||||
return answerHtml;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the answer pre-buttons HTML markup.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*
|
||||
* @return {string} The answer pre-buttons HTML markup.
|
||||
*/
|
||||
getAnswerButtonsPre() {
|
||||
return `
|
||||
<button type="button" class="wpforms-ai-chat-choices-insert wpforms-ai-chat-answer-action wpforms-btn-sm wpforms-btn-orange" >
|
||||
<span>${ chat.modeStrings.insert }</span>
|
||||
</button>
|
||||
`;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the warning message HTML markup.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*
|
||||
* @return {string} The warning message HTML markup.
|
||||
*/
|
||||
getWarningMessage() {
|
||||
// Trigger event before warning message insert.
|
||||
chat.triggerEvent( 'wpformsAIModalBeforeWarningMessageInsert', { fieldId: chat.fieldId } );
|
||||
|
||||
return `<div class="wpforms-ai-chat-divider"></div>
|
||||
<div class="wpforms-chat-item-notice">
|
||||
<div class="wpforms-chat-item-notice-content">
|
||||
<span>${ chat.modeStrings.warning }</span>
|
||||
</div>
|
||||
</div>`;
|
||||
},
|
||||
|
||||
/**
|
||||
* If the field has default choices, the welcome screen is active.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*
|
||||
* @return {boolean} True if the field has default choices, false otherwise.
|
||||
*/
|
||||
isWelcomeScreen() {
|
||||
const items = document.getElementById( `wpforms-field-option-row-${ chat.fieldId }-choices` )
|
||||
.querySelectorAll( 'li input.label' );
|
||||
|
||||
if ( items.length === 1 && ! items[ 0 ].value.trim() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( items.length > 3 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const defaults = Object.values( chat.modeStrings.defaults );
|
||||
|
||||
for ( let i = 0; i < items.length; i++ ) {
|
||||
if ( ! defaults.includes( items[ i ].value ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Add the `choices` answer.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*
|
||||
* @param {HTMLElement} element The answer element.
|
||||
*/
|
||||
addedAnswer( element ) {
|
||||
const button = element.querySelector( '.wpforms-ai-chat-choices-insert' );
|
||||
|
||||
// Listen to the button click event.
|
||||
button?.addEventListener( 'click', this.insertButtonClick.bind( this ) );
|
||||
},
|
||||
|
||||
/**
|
||||
* Sanitize response.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {Object} response The response data to sanitize.
|
||||
*
|
||||
* @return {Object} The sanitized response.
|
||||
*/
|
||||
sanitizeResponse( response ) {
|
||||
if ( ! Array.isArray( response?.choices ) ) {
|
||||
return response;
|
||||
}
|
||||
|
||||
let choices = response.choices;
|
||||
|
||||
// Sanitize choices.
|
||||
choices = choices.map( ( choice ) => {
|
||||
return wpf.sanitizeHTML( choice, wpforms_builder.allowed_label_html_tags );
|
||||
} );
|
||||
|
||||
// Remove empty choices.
|
||||
response.choices = choices.filter( ( choice ) => {
|
||||
return choice.trim() !== '';
|
||||
} );
|
||||
|
||||
return response;
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if the response has a prohibited code.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {Object} response The response data.
|
||||
* @param {Array} sanitizedResponse The sanitized response data.
|
||||
*
|
||||
* @return {boolean} Whether the answer has a prohibited code.
|
||||
*/
|
||||
hasProhibitedCode( response, sanitizedResponse ) {
|
||||
// If the number of choices has changed after sanitization, it means that the answer contains prohibited code.
|
||||
return sanitizedResponse?.choices?.length !== response?.choices?.length;
|
||||
},
|
||||
|
||||
/**
|
||||
* Click on the Use Choices button.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*
|
||||
* @param {Event} e The event object.
|
||||
*/
|
||||
insertButtonClick( e ) {
|
||||
const button = e.target;
|
||||
const answer = button.closest( '.wpforms-chat-item.wpforms-chat-item-choices' );
|
||||
const responseId = answer?.getAttribute( 'data-response-id' );
|
||||
const choicesList = answer?.querySelector( 'ol' );
|
||||
const items = choicesList.querySelectorAll( '.wpforms-ai-chat-choices-item' );
|
||||
const choiceItems = [];
|
||||
|
||||
// Get choices data.
|
||||
for ( const i in items ) {
|
||||
if ( ! items.hasOwnProperty( i ) || ! items[ i ].textContent ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
choiceItems.push( items[ i ].textContent.trim() );
|
||||
}
|
||||
|
||||
// Rate the response.
|
||||
chat.wpformsAiApi.rate( true, responseId );
|
||||
|
||||
// Replace field choices.
|
||||
this.replaceChoices( choiceItems );
|
||||
|
||||
// Toggle to the field.
|
||||
jQuery( `#wpforms-field-${ chat.fieldId }` ).click().promise().done( function() {
|
||||
jQuery( `#wpforms-field-option-basic-${ chat.fieldId } a.wpforms-field-option-group-toggle` ).click();
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Replace field choices.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*
|
||||
* @param {Array} choices Choices array.
|
||||
*/
|
||||
replaceChoices( choices ) {
|
||||
const choicesOptionRow = document.getElementById( `wpforms-field-option-row-${ chat.fieldId }-choices` );
|
||||
const choicesList = choicesOptionRow.querySelector( 'ul.choices-list' );
|
||||
const choiceRow = choicesList.querySelector( 'li:first-child' ).cloneNode( true );
|
||||
|
||||
choiceRow.innerHTML = choiceRow.innerHTML.replace( /\[choices\]\[\d+\]/g, `[choices][{{key}}]` );
|
||||
|
||||
// Clear existing choices.
|
||||
choicesList.innerHTML = '';
|
||||
|
||||
// Add new choices.
|
||||
for ( const i in choices ) {
|
||||
const key = ( Number( i ) + 1 ).toString();
|
||||
const choice = choices[ i ];
|
||||
|
||||
// Clone choice item element.
|
||||
let li = choiceRow.cloneNode( true );
|
||||
|
||||
// Get updated single choice item.
|
||||
li = this.getUpdatedSingleChoiceItem( li, key, choice );
|
||||
|
||||
// Add new choice item.
|
||||
choicesList.appendChild( li );
|
||||
}
|
||||
|
||||
// Update data-next-id attribute for choices list.
|
||||
choicesList.setAttribute( 'data-next-id', choices.length + 1 );
|
||||
|
||||
// Update field preview.
|
||||
const fieldOptions = document.getElementById( `wpforms-field-option-${ chat.fieldId }` );
|
||||
const fieldType = fieldOptions.querySelector( 'input.wpforms-field-option-hidden-type' )?.value;
|
||||
|
||||
WPFormsBuilder.fieldChoiceUpdate( fieldType, chat.fieldId, choices.length );
|
||||
WPFormsBuilder.triggerBuilderEvent( 'wpformsFieldChoiceAdd' );
|
||||
|
||||
// Trigger event after choices insert.
|
||||
chat.triggerEvent( 'wpformsAIModalAfterChoicesInsert', { fieldId: chat.fieldId } );
|
||||
},
|
||||
|
||||
/**
|
||||
* Get updated single choice item.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*
|
||||
* @param {HTMLElement} li Choice item element.
|
||||
* @param {string} key Choice key.
|
||||
* @param {string} choice Choice value.
|
||||
*
|
||||
* @return {HTMLElement} The updated choice item.
|
||||
*/
|
||||
getUpdatedSingleChoiceItem( li, key, choice ) {
|
||||
li.setAttribute( 'data-key', key.toString() );
|
||||
|
||||
// Update choice item inputs name attributes.
|
||||
li.innerHTML = li.innerHTML.replaceAll( '{{key}}', key );
|
||||
|
||||
// Sanitize choice before set.
|
||||
choice = wpf.sanitizeHTML( choice );
|
||||
|
||||
const inputDefault = li.querySelector( 'input.default' );
|
||||
|
||||
inputDefault.removeAttribute( 'checked' );
|
||||
|
||||
// Set label
|
||||
const inputLabel = li.querySelector( 'input.label' );
|
||||
|
||||
inputLabel.value = choice;
|
||||
inputLabel.setAttribute( 'value', choice );
|
||||
|
||||
// Set value.
|
||||
const inputValue = li.querySelector( 'input.value' );
|
||||
|
||||
inputValue.value = choice;
|
||||
inputValue.setAttribute( 'value', choice );
|
||||
|
||||
// Reset image upload.
|
||||
const imageUpload = li.querySelector( '.wpforms-image-upload' );
|
||||
const inputImage = imageUpload.querySelector( 'input.source' );
|
||||
|
||||
inputImage.value = '';
|
||||
inputImage.setAttribute( 'value', '' );
|
||||
imageUpload.querySelector( '.preview' ).innerHTML = '';
|
||||
imageUpload.querySelector( '.wpforms-image-upload-add' ).style.display = 'block';
|
||||
|
||||
// Reset icon choice.
|
||||
const iconSelect = li.querySelector( '.wpforms-icon-select' );
|
||||
|
||||
iconSelect.querySelector( '.ic-fa-preview' ).setAttribute( 'class', 'ic-fa-preview ic-fa-regular ic-fa-face-smile' );
|
||||
iconSelect.querySelector( 'input.source-icon' ).value = 'face-smile';
|
||||
iconSelect.querySelector( 'input.source-icon-style' ).value = 'regular';
|
||||
|
||||
return li;
|
||||
},
|
||||
};
|
||||
}
|
||||
Vendored
Executable
+19
@@ -0,0 +1,19 @@
|
||||
export default function(c){return{getAnswer(e){if(e.choices?.length<1)return"";var t,r=[];for(t in e.choices)r.push(`
|
||||
<li class="wpforms-ai-chat-choices-item">
|
||||
${c.htmlSpecialChars(e.choices[t])}
|
||||
</li>
|
||||
`);let i=`
|
||||
<h4>${c.htmlSpecialChars(e.heading??"")}</h4>
|
||||
<ol>
|
||||
${r.join("")}
|
||||
</ol>
|
||||
`;return c.sessionId||(i+=`<span>${c.modeStrings.footer}</span>`),i},getAnswerButtonsPre(){return`
|
||||
<button type="button" class="wpforms-ai-chat-choices-insert wpforms-ai-chat-answer-action wpforms-btn-sm wpforms-btn-orange" >
|
||||
<span>${c.modeStrings.insert}</span>
|
||||
</button>
|
||||
`},getWarningMessage(){return c.triggerEvent("wpformsAIModalBeforeWarningMessageInsert",{fieldId:c.fieldId}),`<div class="wpforms-ai-chat-divider"></div>
|
||||
<div class="wpforms-chat-item-notice">
|
||||
<div class="wpforms-chat-item-notice-content">
|
||||
<span>${c.modeStrings.warning}</span>
|
||||
</div>
|
||||
</div>`},isWelcomeScreen(){var t=document.getElementById(`wpforms-field-option-row-${c.fieldId}-choices`).querySelectorAll("li input.label");if(1!==t.length||t[0].value.trim()){if(3<t.length)return!1;var r=Object.values(c.modeStrings.defaults);for(let e=0;e<t.length;e++)if(!r.includes(t[e].value))return!1}return!0},addedAnswer(e){e.querySelector(".wpforms-ai-chat-choices-insert")?.addEventListener("click",this.insertButtonClick.bind(this))},sanitizeResponse(t){if(Array.isArray(t?.choices)){let e=t.choices;e=e.map(e=>wpf.sanitizeHTML(e,wpforms_builder.allowed_label_html_tags)),t.choices=e.filter(e=>""!==e.trim())}return t},hasProhibitedCode(e,t){return t?.choices?.length!==e?.choices?.length},insertButtonClick(e){var t,e=e.target.closest(".wpforms-chat-item.wpforms-chat-item-choices"),r=e?.getAttribute("data-response-id"),i=(e?.querySelector("ol")).querySelectorAll(".wpforms-ai-chat-choices-item"),o=[];for(t in i)i.hasOwnProperty(t)&&i[t].textContent&&o.push(i[t].textContent.trim());c.wpformsAiApi.rate(!0,r),this.replaceChoices(o),jQuery("#wpforms-field-"+c.fieldId).click().promise().done(function(){jQuery(`#wpforms-field-option-basic-${c.fieldId} a.wpforms-field-option-group-toggle`).click()})},replaceChoices(e){var t,r=document.getElementById(`wpforms-field-option-row-${c.fieldId}-choices`).querySelector("ul.choices-list"),i=r.querySelector("li:first-child").cloneNode(!0);for(t in i.innerHTML=i.innerHTML.replace(/\[choices\]\[\d+\]/g,"[choices][{{key}}]"),r.innerHTML="",e){var o=(Number(t)+1).toString(),l=e[t],s=i.cloneNode(!0),s=this.getUpdatedSingleChoiceItem(s,o,l);r.appendChild(s)}r.setAttribute("data-next-id",e.length+1);var n=document.getElementById("wpforms-field-option-"+c.fieldId).querySelector("input.wpforms-field-option-hidden-type")?.value;WPFormsBuilder.fieldChoiceUpdate(n,c.fieldId,e.length),WPFormsBuilder.triggerBuilderEvent("wpformsFieldChoiceAdd"),c.triggerEvent("wpformsAIModalAfterChoicesInsert",{fieldId:c.fieldId})},getUpdatedSingleChoiceItem(e,t,r){e.setAttribute("data-key",t.toString()),e.innerHTML=e.innerHTML.replaceAll("{{key}}",t),r=wpf.sanitizeHTML(r);e.querySelector("input.default").removeAttribute("checked");t=e.querySelector("input.label"),t.value=r,t.setAttribute("value",r),t=e.querySelector("input.value"),t.value=r,t.setAttribute("value",r),t=e.querySelector(".wpforms-image-upload"),r=t.querySelector("input.source"),r.value="",r.setAttribute("value",""),t.querySelector(".preview").innerHTML="",t.querySelector(".wpforms-image-upload-add").style.display="block",r=e.querySelector(".wpforms-icon-select");return r.querySelector(".ic-fa-preview").setAttribute("class","ic-fa-preview ic-fa-regular ic-fa-face-smile"),r.querySelector("input.source-icon").value="face-smile",r.querySelector("input.source-icon-style").value="regular",e}}}
|
||||
Executable
+163
@@ -0,0 +1,163 @@
|
||||
/* global WPFormsAIChatHTMLElement, WPFormsAIFormGenerator, wpf, wpforms_builder */
|
||||
|
||||
/**
|
||||
* @param chat.modeStrings.footerFirst
|
||||
* @param chat.modeStrings.inactiveAnswerTitle
|
||||
* @param chat.preventResizeInput
|
||||
* @param response.form_title
|
||||
* @param wpforms_builder.allowed_label_html_tags
|
||||
*/
|
||||
|
||||
/**
|
||||
* The WPForms AI chat element.
|
||||
*
|
||||
* Forms mode helpers module.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {WPFormsAIChatHTMLElement} chat The chat element.
|
||||
*
|
||||
* @return {Object} Forms helpers object.
|
||||
*/
|
||||
export default function( chat ) { // eslint-disable-line no-unused-vars, max-lines-per-function
|
||||
/**
|
||||
* The default `forms` mode helpers object.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
const forms = {
|
||||
/**
|
||||
* Init `forms` mode.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
init() {
|
||||
// Set the initial form generator state.
|
||||
if ( chat.sessionId ) {
|
||||
WPFormsAIFormGenerator.state.chatStart = true;
|
||||
|
||||
// Remove the selected state from the current template card.
|
||||
WPFormsAIFormGenerator.main.el.$templateCard
|
||||
.next( '.selected' ).removeClass( 'selected' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Reset the message input field.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
resetInput() {
|
||||
chat.resizeInput();
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the answer based on AI response data.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {Object} response The AI response data.
|
||||
*
|
||||
* @return {string} HTML markup.
|
||||
*/
|
||||
getAnswer( response ) {
|
||||
if ( ! response ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const rnd = Math.floor( Math.random() * chat.modeStrings.footer.length );
|
||||
const footer = chat.modeStrings.footer[ rnd ];
|
||||
const answer = response.explanation || ( response.form_title ?? '' );
|
||||
|
||||
return `
|
||||
<h4>${ answer }</h4>
|
||||
<span>${ footer }</span>
|
||||
`;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the answer pre-buttons HTML markup.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @return {string} The answer pre-buttons HTML markup.
|
||||
*/
|
||||
getAnswerButtonsPre() {
|
||||
return `
|
||||
<button type="button" class="wpforms-ai-chat-use-form wpforms-ai-chat-answer-action wpforms-btn-sm wpforms-btn-orange" >
|
||||
<span>${ chat.modeStrings.useForm }</span>
|
||||
</button>
|
||||
`;
|
||||
},
|
||||
|
||||
/**
|
||||
* The answer was added.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {HTMLElement} element The answer element.
|
||||
*/
|
||||
addedAnswer( element ) { // eslint-disable-line no-unused-vars
|
||||
forms.updateInactiveAnswers();
|
||||
},
|
||||
|
||||
/**
|
||||
* Set active answer.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {HTMLElement} element The answer element.
|
||||
*/
|
||||
setActiveAnswer( element ) {
|
||||
forms.updateInactiveAnswers();
|
||||
|
||||
element.querySelector( '.wpforms-chat-item-content' ).setAttribute( 'title', '' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Update inactive answers.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
updateInactiveAnswers() {
|
||||
chat.messageList.querySelectorAll( '.wpforms-chat-item-answer:not(.active) .wpforms-chat-item-content' )
|
||||
.forEach( ( el ) => {
|
||||
// Set title attribute for inactive answers.
|
||||
el.setAttribute( 'title', chat.modeStrings.inactiveAnswerTitle );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine whether the Welcome Screen should be displayed.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @return {boolean} Display the Welcome Screen or not.
|
||||
*/
|
||||
isWelcomeScreen() {
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sanitize response.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {Object} response The response data to sanitize.
|
||||
*
|
||||
* @return {Object} The sanitized response.
|
||||
*/
|
||||
sanitizeResponse( response ) {
|
||||
if ( ! response.explanation ) {
|
||||
return response;
|
||||
}
|
||||
|
||||
// Sanitize explanation string.
|
||||
response.explanation = wpf.sanitizeHTML( response.explanation, wpforms_builder.allowed_label_html_tags );
|
||||
|
||||
return response;
|
||||
},
|
||||
};
|
||||
|
||||
return forms;
|
||||
}
|
||||
Vendored
Executable
+8
@@ -0,0 +1,8 @@
|
||||
export default function(r){let t={init(){r.sessionId&&(WPFormsAIFormGenerator.state.chatStart=!0,WPFormsAIFormGenerator.main.el.$templateCard.next(".selected").removeClass("selected"))},resetInput(){r.resizeInput()},getAnswer(e){var t;return e?(t=Math.floor(Math.random()*r.modeStrings.footer.length),t=r.modeStrings.footer[t],`
|
||||
<h4>${e.explanation||(e.form_title??"")}</h4>
|
||||
<span>${t}</span>
|
||||
`):""},getAnswerButtonsPre(){return`
|
||||
<button type="button" class="wpforms-ai-chat-use-form wpforms-ai-chat-answer-action wpforms-btn-sm wpforms-btn-orange" >
|
||||
<span>${r.modeStrings.useForm}</span>
|
||||
</button>
|
||||
`},addedAnswer(e){t.updateInactiveAnswers()},setActiveAnswer(e){t.updateInactiveAnswers(),e.querySelector(".wpforms-chat-item-content").setAttribute("title","")},updateInactiveAnswers(){r.messageList.querySelectorAll(".wpforms-chat-item-answer:not(.active) .wpforms-chat-item-content").forEach(e=>{e.setAttribute("title",r.modeStrings.inactiveAnswerTitle)})},isWelcomeScreen(){return!0},sanitizeResponse(e){return e.explanation&&(e.explanation=wpf.sanitizeHTML(e.explanation,wpforms_builder.allowed_label_html_tags)),e}};return t}
|
||||
Executable
+67
@@ -0,0 +1,67 @@
|
||||
/* global WPFormsAIChatHTMLElement */
|
||||
|
||||
/**
|
||||
* The WPForms AI chat element.
|
||||
*
|
||||
* Choices helpers module.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*
|
||||
* @param {WPFormsAIChatHTMLElement} chat The chat element.
|
||||
*
|
||||
* @return {Function} The app cloning function.
|
||||
*/
|
||||
export default function( chat ) { // eslint-disable-line no-unused-vars
|
||||
/**
|
||||
* The default `text` mode helpers object.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*/
|
||||
return {
|
||||
/**
|
||||
* Get the `text` answer based on AI response data.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*
|
||||
* @param {Object} response The AI response data.
|
||||
*
|
||||
* @return {string} HTML markup.
|
||||
*/
|
||||
getAnswer( response ) {
|
||||
return `
|
||||
<h4>${ response?.heading ?? '' }</h4>
|
||||
<p>${ response?.text ?? '' }</p>
|
||||
<span>${ response?.footer ?? '' }</span>
|
||||
`;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the answer pre-buttons HTML markup.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*
|
||||
* @return {string} The answer pre-buttons HTML markup.
|
||||
*/
|
||||
getAnswerButtonsPre() {
|
||||
return '';
|
||||
},
|
||||
|
||||
/**
|
||||
* Added answer callback.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*/
|
||||
addedAnswer() {},
|
||||
|
||||
/**
|
||||
* Determine whether the Welcome Screen should be displayed.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @return {boolean} Display the Welcome Screen or not.
|
||||
*/
|
||||
isWelcomeScreen() {
|
||||
return true;
|
||||
},
|
||||
};
|
||||
}
|
||||
Vendored
Executable
+5
@@ -0,0 +1,5 @@
|
||||
export default function(e){return{getAnswer(e){return`
|
||||
<h4>${e?.heading??""}</h4>
|
||||
<p>${e?.text??""}</p>
|
||||
<span>${e?.footer??""}</span>
|
||||
`},getAnswerButtonsPre(){return""},addedAnswer(){},isWelcomeScreen(){return!0}}}
|
||||
Executable
+1398
File diff suppressed because it is too large
Load Diff
Vendored
Executable
+43
File diff suppressed because one or more lines are too long
Executable
+245
@@ -0,0 +1,245 @@
|
||||
/* global wpforms_ai_chat_element */
|
||||
|
||||
/**
|
||||
* @param wpforms_ai_chat_element.pinChat
|
||||
* @param wpforms_ai_chat_element.unpinChat
|
||||
* @param wpforms_ai_chat_element.close
|
||||
*/
|
||||
|
||||
/**
|
||||
* Dock JS module.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {jQuery} $ jQuery object.
|
||||
*
|
||||
* @return {Object} Dock object.
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const wpFormsAIDock = ( function( $ ) {
|
||||
/**
|
||||
* Pin modal.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {jQuery} $modal Modal element.
|
||||
*/
|
||||
function pinModal( $modal ) {
|
||||
localStorage.setItem( 'wpforms-ai-chat-prefers-pinned', '1' );
|
||||
|
||||
$modal.find( '.wpforms-ai-modal-pin' ).attr( 'title', wpforms_ai_chat_element.unpinChat );
|
||||
|
||||
const $toolbar = $( '#wpforms-builder-form .wpforms-toolbar' );
|
||||
|
||||
// Get the distance from the top of the screen to the bottom border of the toolbar.
|
||||
const toolbarHeight = $toolbar.offset().top + $toolbar.outerHeight();
|
||||
|
||||
$modal.addClass( 'pinned' );
|
||||
|
||||
if ( $( '#wpadminbar' ).is( ':visible' ) ) {
|
||||
$modal.addClass( 'with-wpadminbar' );
|
||||
}
|
||||
|
||||
$modal.insertAfter( $toolbar ).promise().done( function() {
|
||||
$modal.css( {
|
||||
top: toolbarHeight,
|
||||
} );
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpin modal.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {jQuery} $modal Modal element.
|
||||
*/
|
||||
function unPinModal( $modal ) {
|
||||
localStorage.setItem( 'wpforms-ai-chat-prefers-pinned', '0' );
|
||||
|
||||
$modal.find( '.wpforms-ai-modal-pin' ).attr( 'title', wpforms_ai_chat_element.pinChat );
|
||||
|
||||
$modal.removeClass( 'pinned' );
|
||||
$modal.removeClass( 'with-wpadminbar' );
|
||||
|
||||
$modal.appendTo( $( 'body' ) ).promise().done( function() {
|
||||
$modal.css( {
|
||||
top: 0,
|
||||
} );
|
||||
} );
|
||||
|
||||
$modal.find( '.wpforms-ai-modal-top-bar' ).removeClass( 'scrolled' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle click on the pin button.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
function onPinIconClick() {
|
||||
if ( $( this ).hasClass( 'not-allowed' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const $modal = $( this ).closest( '.jconfirm.jconfirm-wpforms-ai-modal' );
|
||||
|
||||
if ( $modal.hasClass( 'pinned' ) ) {
|
||||
unPinModal( $modal );
|
||||
} else {
|
||||
pinModal( $modal );
|
||||
}
|
||||
|
||||
// Re-apply this action also for other modals but hide them.
|
||||
const $otherModals = $( '.jconfirm.jconfirm-wpforms-ai-modal' ).not( $modal );
|
||||
|
||||
$otherModals.each( function() {
|
||||
const $otherModal = $( this );
|
||||
|
||||
if ( $otherModal.hasClass( 'pinned' ) ) {
|
||||
unPinModal( $otherModal );
|
||||
} else {
|
||||
pinModal( $otherModal );
|
||||
}
|
||||
|
||||
$otherModal.hide();
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle click on the panel toggle button.
|
||||
*
|
||||
* Hide pinned chats if this is not the fields panel.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {Object} clicked Clicked a button object.
|
||||
*/
|
||||
function onPanelToggleClick( clicked ) {
|
||||
const $this = $( clicked.target ),
|
||||
dataPanel = $this.closest( 'button' ).data( 'panel' );
|
||||
|
||||
if ( dataPanel === 'fields' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$( '.jconfirm.jconfirm-wpforms-ai-modal.pinned' ).each( function() {
|
||||
$( this ).hide();
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind events.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
function bindEvents() {
|
||||
const AIModalPin = $( '.wpforms-ai-modal-pin' );
|
||||
|
||||
$( document )
|
||||
.off( 'click', '.wpforms-ai-modal-pin' )
|
||||
.on( 'click', '.wpforms-ai-modal-pin', onPinIconClick )
|
||||
// Hide pin icon for the time of response generation.
|
||||
.on( 'wpformsAIChatBeforeSendMessage', () => AIModalPin.addClass( 'not-allowed' ) )
|
||||
.on( 'wpformsAIChatBeforeError wpformsAIChatAfterTypeText', () => AIModalPin.removeClass( 'not-allowed' ) );
|
||||
|
||||
$( '#wpforms-panels-toggle button' )
|
||||
.off( 'click', onPanelToggleClick )
|
||||
.on( 'click', onPanelToggleClick );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the modal element.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {number} fieldId Field ID.
|
||||
*
|
||||
* @return {jQuery} Modal element.
|
||||
*/
|
||||
function getModal( fieldId ) {
|
||||
const $chatElement = $( 'wpforms-ai-chat[field-id="' + fieldId + '"]' );
|
||||
|
||||
return $chatElement.closest( '.jconfirm.jconfirm-wpforms-ai-modal' ).last();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare dock by adding pin button.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {number} fieldId Field ID.
|
||||
*/
|
||||
function prepareDock( fieldId ) {
|
||||
const $jconfirmAIModal = getModal( fieldId ),
|
||||
dockAlreadyPrepared = $jconfirmAIModal.find( '.wpforms-ai-modal-pin' ).length;
|
||||
|
||||
if ( dockAlreadyPrepared ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const $closeIcon = $jconfirmAIModal.find( '.jconfirm-closeIcon' );
|
||||
|
||||
// Append new bar after close icon and move close icon inside this bar.
|
||||
$closeIcon.after(
|
||||
`<div class="wpforms-ai-modal-top-bar">
|
||||
<div class="wpforms-ai-modal-pin" title="${ wpforms_ai_chat_element.pinChat }"></div>
|
||||
</div>`
|
||||
).promise().done( function() {
|
||||
const $topBar = $jconfirmAIModal.find( '.wpforms-ai-modal-top-bar' );
|
||||
|
||||
$closeIcon.appendTo( $topBar );
|
||||
} );
|
||||
|
||||
$closeIcon.attr( 'title', wpforms_ai_chat_element.close );
|
||||
|
||||
const $topBar = $jconfirmAIModal.find( '.wpforms-ai-modal-top-bar' ),
|
||||
$messageList = $jconfirmAIModal.find( '.wpforms-ai-chat-message-list' );
|
||||
|
||||
$messageList.off( 'scroll' );
|
||||
$messageList.on( 'scroll', function() {
|
||||
if ( $messageList.scrollTop() > 0 ) {
|
||||
$topBar.addClass( 'scrolled' );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$topBar.removeClass( 'scrolled' );
|
||||
} );
|
||||
|
||||
$jconfirmAIModal.on( 'remove', function() {
|
||||
$messageList.off( 'scroll' );
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe pin modal on open.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {string} fieldId Field ID.
|
||||
*/
|
||||
function onOpen( fieldId ) {
|
||||
const savedState = localStorage.getItem( 'wpforms-ai-chat-prefers-pinned' ) || '0';
|
||||
|
||||
if ( savedState === '0' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
pinModal( getModal( fieldId ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the dock.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {number} fieldId Field ID.
|
||||
*/
|
||||
function init( fieldId ) {
|
||||
prepareDock( fieldId );
|
||||
bindEvents();
|
||||
onOpen( fieldId );
|
||||
}
|
||||
|
||||
return { init };
|
||||
}( jQuery ) );
|
||||
Vendored
Executable
+3
@@ -0,0 +1,3 @@
|
||||
let wpFormsAIDock=(e=>{function s(o){localStorage.setItem("wpforms-ai-chat-prefers-pinned","1"),o.find(".wpforms-ai-modal-pin").attr("title",wpforms_ai_chat_element.unpinChat);var a=e("#wpforms-builder-form .wpforms-toolbar");let i=a.offset().top+a.outerHeight();o.addClass("pinned"),e("#wpadminbar").is(":visible")&&o.addClass("with-wpadminbar"),o.insertAfter(a).promise().done(function(){o.css({top:i})})}function a(o){localStorage.setItem("wpforms-ai-chat-prefers-pinned","0"),o.find(".wpforms-ai-modal-pin").attr("title",wpforms_ai_chat_element.pinChat),o.removeClass("pinned"),o.removeClass("with-wpadminbar"),o.appendTo(e("body")).promise().done(function(){o.css({top:0})}),o.find(".wpforms-ai-modal-top-bar").removeClass("scrolled")}function n(){var o;e(this).hasClass("not-allowed")||(((o=e(this).closest(".jconfirm.jconfirm-wpforms-ai-modal")).hasClass("pinned")?a:s)(o),e(".jconfirm.jconfirm-wpforms-ai-modal").not(o).each(function(){var o=e(this);(o.hasClass("pinned")?a:s)(o),o.hide()}))}function t(o){"fields"!==e(o.target).closest("button").data("panel")&&e(".jconfirm.jconfirm-wpforms-ai-modal.pinned").each(function(){e(this).hide()})}function r(o){return e('wpforms-ai-chat[field-id="'+o+'"]').closest(".jconfirm.jconfirm-wpforms-ai-modal").last()}return{init:function(a){{var i=a;let e=r(i),o=e.find(".wpforms-ai-modal-pin").length;if(!o){let a=e.find(".jconfirm-closeIcon"),o=(a.after(`<div class="wpforms-ai-modal-top-bar">
|
||||
<div class="wpforms-ai-modal-pin" title="${wpforms_ai_chat_element.pinChat}"></div>
|
||||
</div>`).promise().done(function(){var o=e.find(".wpforms-ai-modal-top-bar");a.appendTo(o)}),a.attr("title",wpforms_ai_chat_element.close),e.find(".wpforms-ai-modal-top-bar")),i=e.find(".wpforms-ai-chat-message-list");i.off("scroll"),i.on("scroll",function(){0<i.scrollTop()?o.addClass("scrolled"):o.removeClass("scrolled")}),e.on("remove",function(){i.off("scroll")})}}{let o=e(".wpforms-ai-modal-pin");e(document).off("click",".wpforms-ai-modal-pin").on("click",".wpforms-ai-modal-pin",n).on("wpformsAIChatBeforeSendMessage",()=>o.addClass("not-allowed")).on("wpformsAIChatBeforeError wpformsAIChatAfterTypeText",()=>o.removeClass("not-allowed")),e("#wpforms-panels-toggle button").off("click",t).on("click",t)}i=a,"0"!==(localStorage.getItem("wpforms-ai-chat-prefers-pinned")||"0")&&s(r(i))}}})(jQuery);
|
||||
+307
@@ -0,0 +1,307 @@
|
||||
/* global wpforms_ai_chat_element, wpFormsAIDock */
|
||||
|
||||
// noinspection ES6ConvertVarToLetConst
|
||||
/**
|
||||
* AI modal.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*/
|
||||
// eslint-disable-next-line no-var
|
||||
var WPFormsAIModal = window.WPFormsAIModal || ( function( document, window, $ ) {
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const app = {
|
||||
/**
|
||||
* Default modal options.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*/
|
||||
defaultOptions: {
|
||||
title: false,
|
||||
content: '',
|
||||
type: 'ai',
|
||||
smoothContent: true,
|
||||
bgOpacity: 1,
|
||||
boxWidth: 650,
|
||||
contentMaxHeight: 600,
|
||||
closeIcon: true,
|
||||
buttons: false,
|
||||
},
|
||||
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*/
|
||||
init() {
|
||||
$( app.ready );
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialized once the DOM is fully loaded.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*/
|
||||
ready() {
|
||||
app.extendJqueryConfirm();
|
||||
app.bindChoicesActions();
|
||||
},
|
||||
|
||||
/**
|
||||
* Process various events for choices modal.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*/
|
||||
bindChoicesActions() {
|
||||
$( document )
|
||||
.on( 'click', '.wpforms-ai-choices-button', app.initChoicesModal )
|
||||
.on( 'wpformsAIChatBeforeRefreshConfirm', app.beforeChoicesRefreshConfirm )
|
||||
.on( 'wpformsAIModalBeforeWarningMessageInsert', app.refreshModalHeight )
|
||||
.on( 'wpformsAIChatAfterRefresh', app.refreshModalHeight )
|
||||
.on( 'wpformsAIChatCancelRefresh', app.cancelChoicesRefresh )
|
||||
.on( 'wpformsAIChatBeforeSendMessage', function( e ) {
|
||||
app.resizeModalHeight( e.detail.fieldId );
|
||||
} )
|
||||
.on( 'wpformsAIChatAfterAddAnswer', function( e ) {
|
||||
app.resizeModalHeight( e.detail.fieldId );
|
||||
} )
|
||||
.on( 'wpformsAIModalAfterChoicesInsert', function( e ) {
|
||||
app.hideChoicesModal( e.detail.fieldId );
|
||||
} );
|
||||
|
||||
$( window ).on( 'resize', function() {
|
||||
$( '.jconfirm-wpforms-ai-modal wpforms-ai-chat' ).each( function() {
|
||||
app.resizeModalHeight( $( this ).attr( 'field-id' ) );
|
||||
} );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Init modal window.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*
|
||||
* @param {Object} args Modal window arguments.
|
||||
*/
|
||||
initModal( args ) {
|
||||
// Open the modal window.
|
||||
$.confirm( { ...app.defaultOptions, ...args } );
|
||||
},
|
||||
|
||||
/**
|
||||
* Init choices modal window.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*/
|
||||
initChoicesModal() {
|
||||
const $button = $( this );
|
||||
|
||||
if ( $button.hasClass( 'wpforms-prevent-default' ) ) {
|
||||
$button.trigger( 'blur' );
|
||||
return;
|
||||
}
|
||||
|
||||
const fieldId = $button.data( 'field-id' ),
|
||||
$modal = $( `.jconfirm-wpforms-ai-modal-choices-${ fieldId }` );
|
||||
|
||||
// Close any other modals.
|
||||
$( `.jconfirm-wpforms-ai-modal:not(.jconfirm-wpforms-ai-modal-choices-${ fieldId })` )
|
||||
.addClass( 'wpforms-hidden' )
|
||||
.fadeOut();
|
||||
|
||||
if ( $modal.length ) {
|
||||
$modal.removeClass( 'wpforms-hidden' ).fadeIn();
|
||||
return;
|
||||
}
|
||||
|
||||
const args = {},
|
||||
hideChoices = function() {
|
||||
app.hideChoicesModal( fieldId );
|
||||
return false;
|
||||
};
|
||||
|
||||
args.content = `<wpforms-ai-chat mode="choices" field-id="${ fieldId }" />`;
|
||||
args.theme = `wpforms-ai-modal, wpforms-ai-purple, wpforms-ai-modal-choices-${ fieldId }`;
|
||||
args.backgroundDismiss = hideChoices;
|
||||
args.backgroundDismissAnimation = '';
|
||||
args.contentMaxHeight = Math.min( app.defaultOptions.contentMaxHeight, app.getMaxModalHeight() );
|
||||
args.onOpen = function() {
|
||||
// Unbind the click event from the close icon and use our own instead.
|
||||
this.$closeIcon.off( 'click' );
|
||||
this.$closeIcon.on( 'click', hideChoices );
|
||||
};
|
||||
args.onOpenBefore = function() {
|
||||
wpFormsAIDock.init( fieldId );
|
||||
};
|
||||
|
||||
app.initModal( args );
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide the choices modal window.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*
|
||||
* @param {string} fieldId Choice field ID.
|
||||
*/
|
||||
hideChoicesModal( fieldId ) {
|
||||
$( `.jconfirm-wpforms-ai-modal-choices-${ fieldId }` ).addClass( 'wpforms-hidden' ).fadeOut();
|
||||
},
|
||||
|
||||
/**
|
||||
* Show the choices modal window.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*
|
||||
* @param {string} fieldId Choice field ID.
|
||||
*/
|
||||
showChoicesModal( fieldId ) {
|
||||
$( `.jconfirm-wpforms-ai-modal-choices-${ fieldId }` ).removeClass( 'wpforms-hidden' ).fadeIn();
|
||||
},
|
||||
|
||||
/**
|
||||
* Resize choices modal window height.
|
||||
*
|
||||
* @since 1.9.4
|
||||
*
|
||||
* @param {string} fieldId Field ID.
|
||||
*/
|
||||
resizeModalHeight( fieldId ) {
|
||||
const modalHeight = app.getMaxModalHeight();
|
||||
const $modal = $( '.jconfirm-wpforms-ai-modal' ).filter( function() {
|
||||
// find class starts with jconfirm-wpforms-ai-modal- and ends with -{fieldId}.
|
||||
return $( this ).attr( 'class' ).match( new RegExp( 'jconfirm-wpforms-ai-modal-.*-' + fieldId, 'i' ) );
|
||||
} );
|
||||
|
||||
$modal.find( '.jconfirm-content-pane' )
|
||||
.css( {
|
||||
height: modalHeight,
|
||||
'max-height': modalHeight,
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Before choices refresh confirm is displayed.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*
|
||||
* @param {Event} e Event object.
|
||||
*/
|
||||
beforeChoicesRefreshConfirm( e ) {
|
||||
const fieldId = e.detail?.fieldId || 0;
|
||||
|
||||
app.hideChoicesModal( fieldId );
|
||||
},
|
||||
|
||||
/**
|
||||
* Cancel choices' refresh.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*
|
||||
* @param {Event} e Event object.
|
||||
*/
|
||||
cancelChoicesRefresh( e ) {
|
||||
const fieldId = e.detail?.fieldId || 0;
|
||||
|
||||
app.showChoicesModal( fieldId );
|
||||
},
|
||||
|
||||
/**
|
||||
* Refresh the main modal window height.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*
|
||||
* @param {Event} e Event object.
|
||||
*/
|
||||
refreshModalHeight( e ) {
|
||||
const fieldId = e.detail?.fieldId || 0;
|
||||
const maxHeight = Math.min( app.getMaxModalHeight(), app.defaultOptions.contentMaxHeight );
|
||||
|
||||
app.showChoicesModal( fieldId );
|
||||
|
||||
// Reset choices modal window height.
|
||||
$( `.jconfirm-wpforms-ai-modal-choices-${ fieldId } .jconfirm-content-pane` )
|
||||
.css( {
|
||||
height: maxHeight,
|
||||
'max-height': maxHeight,
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the max modal height.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*
|
||||
* @return {number} The max modal height.
|
||||
*/
|
||||
getMaxModalHeight() {
|
||||
// 80% of the window height, but not more than 800 px.
|
||||
return Math.min( $( window ).height() * 0.8, 800 );
|
||||
},
|
||||
|
||||
/**
|
||||
* Extend jquery-confirm plugin with support of max-height for the content area.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*/
|
||||
extendJqueryConfirm() {
|
||||
// Extend a method of global instance.
|
||||
window.Jconfirm.prototype._updateContentMaxHeight = function() {
|
||||
this.$contentPane.css( {
|
||||
'max-height': this.contentMaxHeight + 'px',
|
||||
} );
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Confirm a modal window.
|
||||
*
|
||||
* This is a wrapper for the `jquery.confirm` plugin.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*
|
||||
* @param {Object} args Modal window arguments.
|
||||
*/
|
||||
confirmModal( args ) {
|
||||
const options = {
|
||||
title: false,
|
||||
content: '',
|
||||
icon: 'fa fa-exclamation-circle',
|
||||
type: 'orange',
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: wpforms_ai_chat_element.btnYes,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
action() {
|
||||
if ( typeof args.onConfirm === 'function' ) {
|
||||
args.onConfirm();
|
||||
}
|
||||
},
|
||||
},
|
||||
cancel: {
|
||||
text: wpforms_ai_chat_element.btnCancel,
|
||||
action() {
|
||||
if ( typeof args.onCancel === 'function' ) {
|
||||
args.onCancel();
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
$.confirm( { ...options, ...args } );
|
||||
},
|
||||
};
|
||||
|
||||
// Provide access to public functions/properties.
|
||||
return app;
|
||||
}( document, window, jQuery ) );
|
||||
|
||||
// Initialize.
|
||||
WPFormsAIModal.init();
|
||||
Vendored
Executable
+1
@@ -0,0 +1 @@
|
||||
var WPFormsAIModal=window.WPFormsAIModal||((e,o,t)=>{let n={defaultOptions:{title:!1,content:"",type:"ai",smoothContent:!0,bgOpacity:1,boxWidth:650,contentMaxHeight:600,closeIcon:!0,buttons:!1},init(){t(n.ready)},ready(){n.extendJqueryConfirm(),n.bindChoicesActions()},bindChoicesActions(){t(e).on("click",".wpforms-ai-choices-button",n.initChoicesModal).on("wpformsAIChatBeforeRefreshConfirm",n.beforeChoicesRefreshConfirm).on("wpformsAIModalBeforeWarningMessageInsert",n.refreshModalHeight).on("wpformsAIChatAfterRefresh",n.refreshModalHeight).on("wpformsAIChatCancelRefresh",n.cancelChoicesRefresh).on("wpformsAIChatBeforeSendMessage",function(e){n.resizeModalHeight(e.detail.fieldId)}).on("wpformsAIChatAfterAddAnswer",function(e){n.resizeModalHeight(e.detail.fieldId)}).on("wpformsAIModalAfterChoicesInsert",function(e){n.hideChoicesModal(e.detail.fieldId)}),t(o).on("resize",function(){t(".jconfirm-wpforms-ai-modal wpforms-ai-chat").each(function(){n.resizeModalHeight(t(this).attr("field-id"))})})},initModal(e){t.confirm({...n.defaultOptions,...e})},initChoicesModal(){var o=t(this);if(o.hasClass("wpforms-prevent-default"))o.trigger("blur");else{let i=o.data("field-id"),e=t(".jconfirm-wpforms-ai-modal-choices-"+i);if(t(`.jconfirm-wpforms-ai-modal:not(.jconfirm-wpforms-ai-modal-choices-${i})`).addClass("wpforms-hidden").fadeOut(),e.length)e.removeClass("wpforms-hidden").fadeIn();else{let e={},o=function(){return n.hideChoicesModal(i),!1};e.content=`<wpforms-ai-chat mode="choices" field-id="${i}" />`,e.theme="wpforms-ai-modal, wpforms-ai-purple, wpforms-ai-modal-choices-"+i,e.backgroundDismiss=o,e.backgroundDismissAnimation="",e.contentMaxHeight=Math.min(n.defaultOptions.contentMaxHeight,n.getMaxModalHeight()),e.onOpen=function(){this.$closeIcon.off("click"),this.$closeIcon.on("click",o)},e.onOpenBefore=function(){wpFormsAIDock.init(i)},n.initModal(e)}}},hideChoicesModal(e){t(".jconfirm-wpforms-ai-modal-choices-"+e).addClass("wpforms-hidden").fadeOut()},showChoicesModal(e){t(".jconfirm-wpforms-ai-modal-choices-"+e).removeClass("wpforms-hidden").fadeIn()},resizeModalHeight(e){var o=n.getMaxModalHeight();t(".jconfirm-wpforms-ai-modal").filter(function(){return t(this).attr("class").match(new RegExp("jconfirm-wpforms-ai-modal-.*-"+e,"i"))}).find(".jconfirm-content-pane").css({height:o,"max-height":o})},beforeChoicesRefreshConfirm(e){e=e.detail?.fieldId||0;n.hideChoicesModal(e)},cancelChoicesRefresh(e){e=e.detail?.fieldId||0;n.showChoicesModal(e)},refreshModalHeight(e){var e=e.detail?.fieldId||0,o=Math.min(n.getMaxModalHeight(),n.defaultOptions.contentMaxHeight);n.showChoicesModal(e),t(`.jconfirm-wpforms-ai-modal-choices-${e} .jconfirm-content-pane`).css({height:o,"max-height":o})},getMaxModalHeight(){return Math.min(.8*t(o).height(),800)},extendJqueryConfirm(){o.Jconfirm.prototype._updateContentMaxHeight=function(){this.$contentPane.css({"max-height":this.contentMaxHeight+"px"})}},confirmModal(e){var o={title:!1,content:"",icon:"fa fa-exclamation-circle",type:"orange",buttons:{confirm:{text:wpforms_ai_chat_element.btnYes,btnClass:"btn-confirm",keys:["enter"],action(){"function"==typeof e.onConfirm&&e.onConfirm()}},cancel:{text:wpforms_ai_chat_element.btnCancel,action(){"function"==typeof e.onCancel&&e.onCancel()}}}};t.confirm({...o,...e})}};return n})(document,window,jQuery);WPFormsAIModal.init();
|
||||
Executable
+223
@@ -0,0 +1,223 @@
|
||||
/* global wpforms_ai_form_generator, wpf, wpforms_ai_chat_element, WPFormsBuilder */
|
||||
|
||||
/**
|
||||
* @param strings.dismissed.installAddons
|
||||
* @param strings.isLicenseActive
|
||||
* @param strings.modules.main
|
||||
* @param strings.templateCard.buttonTextInit
|
||||
* @param strings.templateCard.imageSrc
|
||||
* @param strings.liteConnectAllowed
|
||||
* @param strings.liteConnectEnabled
|
||||
* @param strings.liteConnectNotAllowed
|
||||
* @param window.WPFormsAIFormGenerator
|
||||
* @param wpforms_builder.is_ai_disabled
|
||||
*/
|
||||
|
||||
// noinspection ES6ConvertVarToLetConst
|
||||
/**
|
||||
* WPForms AI Form Generator.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
// eslint-disable-next-line no-var
|
||||
var WPFormsAIFormGenerator = window.WPFormsAIFormGenerator || ( function( document, window, $ ) {
|
||||
/**
|
||||
* Localized strings.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const strings = wpforms_ai_form_generator;
|
||||
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const app = {
|
||||
/**
|
||||
* State data holder.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
state: {},
|
||||
|
||||
/**
|
||||
* Main module.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
main: null,
|
||||
|
||||
/**
|
||||
* The form preview module.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
preview: null,
|
||||
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
init() {
|
||||
if ( window.wpforms_builder?.is_ai_disabled || app.isLoaded ) {
|
||||
return;
|
||||
}
|
||||
|
||||
app.updateLocationUrl();
|
||||
app.events();
|
||||
|
||||
app.isLoaded = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Events.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
events() {
|
||||
$( document )
|
||||
.on( 'wpformsSetupPanelBeforeInitTemplatesList', app.addTemplateCard );
|
||||
|
||||
$( '#wpforms-builder' )
|
||||
.on( 'wpformsBuilderReady', app.maybeSaveForm )
|
||||
.on( 'wpformsBuilderPanelLoaded', app.panelLoaded );
|
||||
},
|
||||
|
||||
/**
|
||||
* Panel loaded event.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {Object} e Event object.
|
||||
* @param {string} panel Panel name.
|
||||
*/
|
||||
panelLoaded( e, panel ) {
|
||||
if ( panel !== 'setup' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load generator modules and run the main module.
|
||||
Promise.all( [
|
||||
import( strings.modules.main ),
|
||||
import( strings.modules.preview ),
|
||||
import( strings.modules.modals ),
|
||||
] )
|
||||
.then( ( [ moduleMain, modulePreview, moduleModals ] ) => {
|
||||
app.main = moduleMain.default( app, $ );
|
||||
app.preview = modulePreview.default( app, $ );
|
||||
app.modals = moduleModals.default( app, $ );
|
||||
|
||||
// Run the main module.
|
||||
app.main.init();
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Add the generator template card to the list.
|
||||
*
|
||||
* At this point, before the list is rendered, we can add our card.
|
||||
* The card will be added to the top of the list.
|
||||
* Event handlers will be attached later by the main module.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
addTemplateCard() {
|
||||
if ( $( '#wpforms-template-generate' ).length ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$( '#wpforms-setup-templates-list .list' ).prepend( app.renderTemplateCard() );
|
||||
|
||||
wpf.initTooltips( $( '#wpforms-template-generate .wpforms-template-buttons' ) );
|
||||
},
|
||||
|
||||
/**
|
||||
* Render the template card HTML.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @return {string} The card markup.
|
||||
*/
|
||||
renderTemplateCard() { // eslint-disable-line complexity
|
||||
const cardClass = window.wpforms_builder?.template_slug === 'generate' ? 'selected' : '';
|
||||
|
||||
let buttonAttr = '';
|
||||
let buttonClass = ! Object.keys( strings.addonsData ).length || strings.dismissed.installAddons
|
||||
? 'wpforms-template-generate'
|
||||
: 'wpforms-template-generate-install-addons';
|
||||
|
||||
// In Lite, we should disable the button in the case Lite Connect is not allowed.
|
||||
if ( ! strings.isPro && ! strings.liteConnectAllowed ) {
|
||||
buttonClass += ' wpforms-inactive wpforms-help-tooltip wpforms-prevent-default';
|
||||
buttonAttr = `data-tooltip-position="top" title="${ strings.templateCard.liteConnectNotAllowed }"`;
|
||||
}
|
||||
|
||||
// In Lite, we should show the modal to enable Lite Connect if it is allowed.
|
||||
if ( ! strings.isPro && ! strings.liteConnectEnabled && strings.liteConnectAllowed ) {
|
||||
buttonClass += ' enable-lite-connect-modal wpforms-prevent-default';
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="wpforms-template ${ cardClass }" id="wpforms-template-generate">
|
||||
<div class="wpforms-template-thumbnail">
|
||||
<div class="wpforms-template-thumbnail-placeholder">
|
||||
<img src="${ strings.templateCard.imageSrc }" alt="${ strings.templateCard.name }" loading="lazy">
|
||||
</div>
|
||||
</div>
|
||||
<div class="wpforms-template-name-wrap">
|
||||
<h3 class="wpforms-template-name categories has-access favorite slug subcategories fields" data-categories="all,new" data-subcategories="" data-fields="" data-has-access="1" data-favorite="" data-slug="generate">
|
||||
${ strings.templateCard.name }
|
||||
</h3>
|
||||
<span class="wpforms-badge wpforms-badge-sm wpforms-badge-inline wpforms-badge-purple wpforms-badge-rounded">${ strings.templateCard.new }</span>
|
||||
</div>
|
||||
<p class="wpforms-template-desc">
|
||||
${ strings.templateCard.desc }
|
||||
</p>
|
||||
<div class="wpforms-template-buttons">
|
||||
<a href="#" class="${ buttonClass } wpforms-btn wpforms-btn-md wpforms-btn-purple-dark" ${ buttonAttr }>
|
||||
${ strings.templateCard.buttonTextInit }
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
|
||||
/**
|
||||
* Save the form when the generated form opened.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
maybeSaveForm() {
|
||||
// Only in case the generated form was used, we have a chat session in the localized vars.
|
||||
if ( wpforms_ai_chat_element.forms?.chatHtml && ! wpf.getQueryString( 'newform' ) ) {
|
||||
WPFormsBuilder.formSave( false );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove the session from URL.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
updateLocationUrl() {
|
||||
history.replaceState( {}, null, wpf.updateQueryString( 'session', null ) );
|
||||
},
|
||||
};
|
||||
|
||||
return app;
|
||||
}( document, window, jQuery ) );
|
||||
|
||||
// Initialize.
|
||||
WPFormsAIFormGenerator.init();
|
||||
Vendored
Executable
+23
@@ -0,0 +1,23 @@
|
||||
var WPFormsAIFormGenerator=window.WPFormsAIFormGenerator||((e,r,s)=>{let l=wpforms_ai_form_generator,o={state:{},main:null,preview:null,init(){r.wpforms_builder?.is_ai_disabled||o.isLoaded||(o.updateLocationUrl(),o.events(),o.isLoaded=!0)},events(){s(e).on("wpformsSetupPanelBeforeInitTemplatesList",o.addTemplateCard),s("#wpforms-builder").on("wpformsBuilderReady",o.maybeSaveForm).on("wpformsBuilderPanelLoaded",o.panelLoaded)},panelLoaded(e,t){"setup"===t&&Promise.all([import(l.modules.main),import(l.modules.preview),import(l.modules.modals)]).then(([e,t,a])=>{o.main=e.default(o,s),o.preview=t.default(o,s),o.modals=a.default(o,s),o.main.init()})},addTemplateCard(){s("#wpforms-template-generate").length||(s("#wpforms-setup-templates-list .list").prepend(o.renderTemplateCard()),wpf.initTooltips(s("#wpforms-template-generate .wpforms-template-buttons")))},renderTemplateCard(){var e="generate"===r.wpforms_builder?.template_slug?"selected":"";let t="",a=!Object.keys(l.addonsData).length||l.dismissed.installAddons?"wpforms-template-generate":"wpforms-template-generate-install-addons";return l.isPro||l.liteConnectAllowed||(a+=" wpforms-inactive wpforms-help-tooltip wpforms-prevent-default",t=`data-tooltip-position="top" title="${l.templateCard.liteConnectNotAllowed}"`),l.isPro||l.liteConnectEnabled||!l.liteConnectAllowed||(a+=" enable-lite-connect-modal wpforms-prevent-default"),`
|
||||
<div class="wpforms-template ${e}" id="wpforms-template-generate">
|
||||
<div class="wpforms-template-thumbnail">
|
||||
<div class="wpforms-template-thumbnail-placeholder">
|
||||
<img src="${l.templateCard.imageSrc}" alt="${l.templateCard.name}" loading="lazy">
|
||||
</div>
|
||||
</div>
|
||||
<div class="wpforms-template-name-wrap">
|
||||
<h3 class="wpforms-template-name categories has-access favorite slug subcategories fields" data-categories="all,new" data-subcategories="" data-fields="" data-has-access="1" data-favorite="" data-slug="generate">
|
||||
${l.templateCard.name}
|
||||
</h3>
|
||||
<span class="wpforms-badge wpforms-badge-sm wpforms-badge-inline wpforms-badge-purple wpforms-badge-rounded">${l.templateCard.new}</span>
|
||||
</div>
|
||||
<p class="wpforms-template-desc">
|
||||
${l.templateCard.desc}
|
||||
</p>
|
||||
<div class="wpforms-template-buttons">
|
||||
<a href="#" class="${a} wpforms-btn wpforms-btn-md wpforms-btn-purple-dark" ${t}>
|
||||
${l.templateCard.buttonTextInit}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
`},maybeSaveForm(){wpforms_ai_chat_element.forms?.chatHtml&&!wpf.getQueryString("newform")&&WPFormsBuilder.formSave(!1)},updateLocationUrl(){history.replaceState({},null,wpf.updateQueryString("session",null))}};return o})(document,window,jQuery);WPFormsAIFormGenerator.init();
|
||||
+563
@@ -0,0 +1,563 @@
|
||||
/* global wpf, wpforms_ai_form_generator, wpforms_ai_chat_element, WPFormsBuilder, wpforms_builder, WPFormsChallenge */
|
||||
|
||||
/**
|
||||
* @param strings.panel.backToTemplates
|
||||
* @param strings.panel.emptyStateDesc
|
||||
* @param strings.panel.emptyStateTitle
|
||||
* @param strings.templateCard.buttonTextContinue
|
||||
* @param wpforms_ai_chat_element.forms.responseHistory
|
||||
* @param wpforms_builder.template_slug
|
||||
*/
|
||||
|
||||
/**
|
||||
* The WPForms AI form generator app.
|
||||
*
|
||||
* Main module.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {Object} generator The AI form generator.
|
||||
* @param {Object} $ jQuery function.
|
||||
*
|
||||
* @return {Object} The main module object.
|
||||
*/
|
||||
export default function( generator, $ ) { // eslint-disable-line max-lines-per-function
|
||||
/**
|
||||
* Localized strings.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const strings = wpforms_ai_form_generator;
|
||||
|
||||
/**
|
||||
* The main module object.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
const main = {
|
||||
/**
|
||||
* DOM elements.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
el: {},
|
||||
|
||||
/**
|
||||
* Init generator.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
init() {
|
||||
main.initState();
|
||||
main.initElementsCache();
|
||||
main.initStateProxy();
|
||||
|
||||
// Magic, we just need to set the state property to `true` to add the panel to the DOM.
|
||||
generator.state.panelAdd = true;
|
||||
|
||||
generator.preview.init();
|
||||
generator.modals.init();
|
||||
main.events();
|
||||
},
|
||||
|
||||
/**
|
||||
* Init generator state.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
initState() {
|
||||
generator.state = {
|
||||
formId: $( '#wpforms-builder-form' ).data( 'id' ),
|
||||
panelAdd: false,
|
||||
panelOpen: false,
|
||||
chatStart: false,
|
||||
aiResponse: null,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Events.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
events() {
|
||||
// Setup panel events.
|
||||
main.el.$setupPanel
|
||||
.on( 'click', '.wpforms-template-generate', main.event.clickGenerateFormBtn )
|
||||
.on( 'click', '.wpforms-template-generate-install-addons', generator.modals.openAddonsModal );
|
||||
|
||||
// Generator panel events.
|
||||
main.el.$generatorPanel
|
||||
.on( 'click', '.wpforms-btn-back-to-templates', main.event.clickBackToTemplatesBtn )
|
||||
.on( 'click', '.wpforms-ai-chat-reload-link', main.event.reloadPage )
|
||||
.on( 'click', '.wpforms-ai-chat-use-form', main.event.useForm );
|
||||
|
||||
// The Form Builder events
|
||||
main.el.$builder
|
||||
.on( 'wpformsPanelSwitch', main.event.panelSwitch );
|
||||
|
||||
// AI chat events.
|
||||
main.el.$doc
|
||||
.on( 'wpformsBuilderReady', main.maybeOpenPanel )
|
||||
.on( 'wpformsAIChatBeforeAddAnswer', main.event.chatBeforeAddAnswer )
|
||||
.on( 'wpformsAIChatAddedAnswer', main.event.chatAddedAnswer )
|
||||
.on( 'wpformsAIChatAfterRefresh', main.event.chatAfterRefresh )
|
||||
.on( 'wpformsAIChatSetActiveAnswer', main.event.chatSetActiveAnswer );
|
||||
},
|
||||
|
||||
/**
|
||||
* Init elements cache.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
initElementsCache() {
|
||||
// Cache DOM elements.
|
||||
main.el.$doc = $( document );
|
||||
main.el.$builder = $( '#wpforms-builder' );
|
||||
main.el.$builderToolbar = $( '#wpforms-builder .wpforms-toolbar' );
|
||||
main.el.$templatesList = $( '#wpforms-setup-templates-list .list' ); // The templates list container.
|
||||
main.el.$templateCard = $( '#wpforms-template-generate' ); // The generator template card.
|
||||
main.el.$generatorPanel = $( '#wpforms-panel-ai-form' ); // The generator panel.
|
||||
main.el.$setupPanel = $( '#wpforms-panel-setup' ); // The Setup panel.
|
||||
main.el.$panelsContainer = $( '.wpforms-panels' ); // All panels container.
|
||||
main.el.$allPanels = $( '.wpforms-panel' ); // All panels.
|
||||
main.el.$chat = main.el.$generatorPanel.find( 'wpforms-ai-chat .wpforms-ai-chat' ); // The chat container.
|
||||
},
|
||||
|
||||
/**
|
||||
* Init state proxy.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
initStateProxy() {
|
||||
generator.state = new Proxy( generator.state, {
|
||||
set( state, key, value ) {
|
||||
// Set the state property.
|
||||
state[ key ] = value;
|
||||
|
||||
if ( typeof main.setStateHandler[ key ] !== 'function' ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Run the set state property handler.
|
||||
main.setStateHandler[ key ]( value );
|
||||
|
||||
// Debug log.
|
||||
wpf.debug( 'Form Generator state changed:', key, '=', value );
|
||||
|
||||
return true;
|
||||
},
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handlers
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
event: {
|
||||
/**
|
||||
* Click on the `Generate Form` button.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {Object} e Event object.
|
||||
*/
|
||||
clickGenerateFormBtn( e ) {
|
||||
e.preventDefault();
|
||||
|
||||
if ( $( this ).hasClass( 'wpforms-prevent-default' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Open the Form Generator panel.
|
||||
generator.state.panelOpen = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Click on the `Back to Templates` button.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
clickBackToTemplatesBtn() {
|
||||
// Close the Form Generator panel.
|
||||
generator.state.panelOpen = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Before adding the answer to the chat.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {Object} e Event object.
|
||||
*/
|
||||
chatBeforeAddAnswer( e ) {
|
||||
// Store the AI response data in state.
|
||||
generator.state.aiResponse = e.originalEvent.detail?.response;
|
||||
generator.state.aiResponseHistory = generator.state.aiResponseHistory || {};
|
||||
generator.state.aiResponseHistory[ generator.state.aiResponse?.responseId ] = generator.state.aiResponse;
|
||||
},
|
||||
|
||||
/**
|
||||
* The answer added to the chat.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {Object} e Event object.
|
||||
*/
|
||||
chatAddedAnswer( e ) {
|
||||
const chat = e.originalEvent.detail?.chat || {};
|
||||
|
||||
// Set chatStart state.
|
||||
if ( chat?.sessionId && ! generator.state.chatStart ) {
|
||||
generator.state.chatStart = true;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Refresh the chat triggered.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
chatAfterRefresh() {
|
||||
generator.preview.clear();
|
||||
},
|
||||
|
||||
/**
|
||||
* Set active answer. Switch form preview to the active answer.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {Object} e Event object.
|
||||
*/
|
||||
chatSetActiveAnswer( e ) {
|
||||
generator.state.aiResponse = generator.state.aiResponseHistory[ e.originalEvent.detail?.responseId ];
|
||||
},
|
||||
|
||||
/**
|
||||
* Click on the "use this form" button.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {Object} e Event object.
|
||||
*/
|
||||
useForm( e ) {
|
||||
e?.preventDefault();
|
||||
|
||||
const $button = $( this );
|
||||
const formId = generator.state.formId;
|
||||
|
||||
if ( ! formId || wpforms_builder.template_slug === 'generate' ) {
|
||||
main.useFormAjax( $button );
|
||||
} else {
|
||||
generator.modals.openExistingFormModal( $button );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Click on the "reload" link.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {Object} e Event object.
|
||||
*/
|
||||
reloadPage( e ) {
|
||||
e?.preventDefault();
|
||||
window.location = window.location + '&ai-form';
|
||||
},
|
||||
|
||||
/**
|
||||
* Switch the Form Builder panel.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
panelSwitch() {
|
||||
generator.state.panelOpen = false;
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Set state property handlers.
|
||||
*
|
||||
* Each handler runs when the appropriate state property was set.
|
||||
* For example, when `panelAdd` state property was set, the `setStateHandler.panelAdd()` handler will run.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
setStateHandler: {
|
||||
/**
|
||||
* `panelAdd` state handler.
|
||||
*
|
||||
* When the value is `true`, the panel will be added to the DOM, otherwise removed.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {boolean} value The state value.
|
||||
*/
|
||||
panelAdd( value ) {
|
||||
// Remove the panel from DOM.
|
||||
if ( ! value ) {
|
||||
main.el.$generatorPanel?.remove();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// The panel already added, no need to add again.
|
||||
if ( main.el.$generatorPanel?.length ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add panel to DOM.
|
||||
main.el.$panelsContainer.append( main.render.generatorPanel() );
|
||||
|
||||
// Cache elements.
|
||||
main.el.$generatorPanel = $( '#wpforms-panel-ai-form' );
|
||||
main.el.$chat = main.el.$generatorPanel.find( 'wpforms-ai-chat .wpforms-ai-chat' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Panel open state handler.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {boolean} value The state value.
|
||||
*/
|
||||
panelOpen( value ) {
|
||||
main.el.$generatorPanel.toggleClass( 'active', value );
|
||||
main.el.$templateCard.addClass( 'selected' );
|
||||
main.setToolbarState( value );
|
||||
|
||||
// Freeze/unfreeze the Challenge.
|
||||
window.WPFormsChallenge?.core.freezeChallenge( value, strings.misc.frozenChallengeTooltip );
|
||||
$( 'body' ).toggleClass( 'wpforms-ai-form-generator-active', value );
|
||||
|
||||
if (
|
||||
generator.state.aiResponseHistory ||
|
||||
! wpforms_ai_chat_element.forms.responseHistory
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the response history if it exists.
|
||||
generator.state.aiResponseHistory = wpforms_ai_chat_element.forms.responseHistory;
|
||||
|
||||
const $activeResponse = main.el.$chat.find( '.wpforms-chat-item-answer.active' );
|
||||
const activeResponseId = $activeResponse.data( 'response-id' );
|
||||
|
||||
generator.state.aiResponse = generator.state.aiResponseHistory[ activeResponseId ];
|
||||
|
||||
// Scroll to the active response.
|
||||
$activeResponse[ 0 ].scrollIntoView( { behavior: 'smooth', block: 'end' } );
|
||||
},
|
||||
|
||||
/**
|
||||
* Chat start state handler.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {boolean} value The state value.
|
||||
*/
|
||||
chatStart( value ) {
|
||||
if ( ! value ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the generator template card button text.
|
||||
main.el.$templateCard
|
||||
.addClass( 'selected' )
|
||||
.find( '.wpforms-template-generate' )
|
||||
.text( strings.templateCard.buttonTextContinue );
|
||||
},
|
||||
|
||||
/**
|
||||
* AI response state handler.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {Object} response The response data.
|
||||
*/
|
||||
aiResponse( response ) {
|
||||
if ( ! response ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the preview.
|
||||
generator.preview.update();
|
||||
},
|
||||
|
||||
/**
|
||||
* Is the form preview update in progress.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {boolean} value Flag value.
|
||||
*/
|
||||
isPreviewUpdate( value ) {
|
||||
main.el.$chat.toggleClass( 'wpforms-ai-chat-inactive', value );
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* HTML renderers.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
render: {
|
||||
/**
|
||||
* Render generator panel HTML.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @return {string} The panel markup.
|
||||
*/
|
||||
generatorPanel() {
|
||||
return `
|
||||
<div class="wpforms-panel wpforms-panel-fields" id="wpforms-panel-ai-form">
|
||||
<div class="wpforms-panel-sidebar-content">
|
||||
<div class="wpforms-panel-sidebar">
|
||||
<div class="wpforms-panel-sidebar-header">
|
||||
<button type="button" class="wpforms-btn-back-to-templates" aria-label="${ strings.panel.backToTemplates }">
|
||||
${ strings.panel.backToTemplates }
|
||||
</button>
|
||||
</div>
|
||||
<wpforms-ai-chat mode="forms" class="wpforms-ai-chat-blue"/>
|
||||
</div>
|
||||
<div class="wpforms-panel-content-wrap">
|
||||
<div class="wpforms-panel-content">
|
||||
<div class="wpforms-panel-empty-state">
|
||||
<h4>${ strings.panel.emptyStateTitle }</h4>
|
||||
<p>${ strings.panel.emptyStateDesc }</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Maybe open the form generator panel.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
maybeOpenPanel() {
|
||||
// Open the panel only if the `ai-form` query string parameter exists.
|
||||
if ( ! window.location.search.includes( '&ai-form' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the query string parameter from the URL.
|
||||
history.replaceState( {}, null, wpf.updateQueryString( 'ai-form', null ) );
|
||||
|
||||
// Open the LiteConnect modal if it is not enabled.
|
||||
const $buttonLiteConnect = $( '.wpforms-template-generate.enable-lite-connect-modal' );
|
||||
|
||||
if ( $buttonLiteConnect.length ) {
|
||||
setTimeout(
|
||||
function() {
|
||||
$buttonLiteConnect.trigger( 'click' );
|
||||
},
|
||||
0
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Open the panel if all addons are installed OR the modal is dismissed.
|
||||
if ( ! Object.keys( strings.addonsData ).length || strings.dismissed.installAddons ) {
|
||||
generator.state.panelOpen = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Open the addons install modal.
|
||||
generator.modals.openAddonsModal( null );
|
||||
},
|
||||
|
||||
/**
|
||||
* The "Use this form" ajax call.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {jQuery} $button Button element.
|
||||
*/
|
||||
useFormAjax( $button ) {
|
||||
const sessionId = $button.closest( '.wpforms-ai-chat' ).data( 'session-id' );
|
||||
const responseId = $button.closest( '.wpforms-chat-item' ).data( 'response-id' );
|
||||
|
||||
WPFormsBuilder.showLoadingOverlay();
|
||||
|
||||
// Rate the response.
|
||||
main.getChatElement()?.wpformsAiApi.rate( true, responseId );
|
||||
|
||||
// Do not display the alert about unsaved changes.
|
||||
WPFormsBuilder.setCloseConfirmation( false );
|
||||
|
||||
const data = {
|
||||
action: 'wpforms_use_ai_form',
|
||||
nonce: strings.nonce,
|
||||
formId: generator.state.formId,
|
||||
formData: generator.state.aiResponseHistory[ responseId ],
|
||||
sessionId,
|
||||
chatHtml: $button.closest( 'wpforms-ai-chat' ).html(),
|
||||
responseHistory: generator.state.aiResponseHistory,
|
||||
};
|
||||
|
||||
generator.preview.closeTooltips();
|
||||
|
||||
$.post( strings.ajaxUrl, data )
|
||||
.done( function( res ) {
|
||||
if ( ! res.success ) {
|
||||
wpf.debug( 'Form Generator AJAX error:', res.data.error ?? res.data );
|
||||
return;
|
||||
}
|
||||
|
||||
const newForm = ! data.formId ? '&newform=1' : '';
|
||||
|
||||
if ( ! window.WPFormsChallenge ) {
|
||||
window.location.assign( res.data.redirect + newForm );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// When the Challenge is active, we need to resume it and continue the steps.
|
||||
WPFormsChallenge.core.resumeChallengeAndExec( {}, () => {
|
||||
WPFormsChallenge.core.stepCompleted( 2 )
|
||||
.done( () => {
|
||||
window.location.assign( res.data.redirect + newForm );
|
||||
} );
|
||||
} );
|
||||
} )
|
||||
.fail( function( xhr ) {
|
||||
wpf.debug( 'Form Generator AJAX error:', xhr.responseText ?? xhr.statusText );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the Builder's toolbar state.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {boolean} isEmpty The toolbar is empty.
|
||||
*/
|
||||
setToolbarState( isEmpty ) {
|
||||
main.el.$builderToolbar.toggleClass( 'empty', isEmpty );
|
||||
main.el.$builderToolbar.find( '.js-wpforms-help span' ).toggleClass( 'screen-reader-text', ! isEmpty );
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the AI chat element.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @return {HTMLElement} The chat element.
|
||||
*/
|
||||
getChatElement() {
|
||||
return main.el.$chat.parent()[ 0 ];
|
||||
},
|
||||
};
|
||||
|
||||
return main;
|
||||
}
|
||||
Vendored
Executable
+22
@@ -0,0 +1,22 @@
|
||||
export default function(o,n){let r=wpforms_ai_form_generator,l={el:{},init(){l.initState(),l.initElementsCache(),l.initStateProxy(),o.state.panelAdd=!0,o.preview.init(),o.modals.init(),l.events()},initState(){o.state={formId:n("#wpforms-builder-form").data("id"),panelAdd:!1,panelOpen:!1,chatStart:!1,aiResponse:null}},events(){l.el.$setupPanel.on("click",".wpforms-template-generate",l.event.clickGenerateFormBtn).on("click",".wpforms-template-generate-install-addons",o.modals.openAddonsModal),l.el.$generatorPanel.on("click",".wpforms-btn-back-to-templates",l.event.clickBackToTemplatesBtn).on("click",".wpforms-ai-chat-reload-link",l.event.reloadPage).on("click",".wpforms-ai-chat-use-form",l.event.useForm),l.el.$builder.on("wpformsPanelSwitch",l.event.panelSwitch),l.el.$doc.on("wpformsBuilderReady",l.maybeOpenPanel).on("wpformsAIChatBeforeAddAnswer",l.event.chatBeforeAddAnswer).on("wpformsAIChatAddedAnswer",l.event.chatAddedAnswer).on("wpformsAIChatAfterRefresh",l.event.chatAfterRefresh).on("wpformsAIChatSetActiveAnswer",l.event.chatSetActiveAnswer)},initElementsCache(){l.el.$doc=n(document),l.el.$builder=n("#wpforms-builder"),l.el.$builderToolbar=n("#wpforms-builder .wpforms-toolbar"),l.el.$templatesList=n("#wpforms-setup-templates-list .list"),l.el.$templateCard=n("#wpforms-template-generate"),l.el.$generatorPanel=n("#wpforms-panel-ai-form"),l.el.$setupPanel=n("#wpforms-panel-setup"),l.el.$panelsContainer=n(".wpforms-panels"),l.el.$allPanels=n(".wpforms-panel"),l.el.$chat=l.el.$generatorPanel.find("wpforms-ai-chat .wpforms-ai-chat")},initStateProxy(){o.state=new Proxy(o.state,{set(e,t,a){return e[t]=a,"function"==typeof l.setStateHandler[t]&&(l.setStateHandler[t](a),wpf.debug("Form Generator state changed:",t,"=",a)),!0}})},event:{clickGenerateFormBtn(e){e.preventDefault(),n(this).hasClass("wpforms-prevent-default")||(o.state.panelOpen=!0)},clickBackToTemplatesBtn(){o.state.panelOpen=!1},chatBeforeAddAnswer(e){o.state.aiResponse=e.originalEvent.detail?.response,o.state.aiResponseHistory=o.state.aiResponseHistory||{},o.state.aiResponseHistory[o.state.aiResponse?.responseId]=o.state.aiResponse},chatAddedAnswer(e){(e.originalEvent.detail?.chat||{})?.sessionId&&!o.state.chatStart&&(o.state.chatStart=!0)},chatAfterRefresh(){o.preview.clear()},chatSetActiveAnswer(e){o.state.aiResponse=o.state.aiResponseHistory[e.originalEvent.detail?.responseId]},useForm(e){e?.preventDefault();e=n(this);o.state.formId&&"generate"!==wpforms_builder.template_slug?o.modals.openExistingFormModal(e):l.useFormAjax(e)},reloadPage(e){e?.preventDefault(),window.location=window.location+"&ai-form"},panelSwitch(){o.state.panelOpen=!1}},setStateHandler:{panelAdd(e){e?l.el.$generatorPanel?.length||(l.el.$panelsContainer.append(l.render.generatorPanel()),l.el.$generatorPanel=n("#wpforms-panel-ai-form"),l.el.$chat=l.el.$generatorPanel.find("wpforms-ai-chat .wpforms-ai-chat")):l.el.$generatorPanel?.remove()},panelOpen(e){var t;l.el.$generatorPanel.toggleClass("active",e),l.el.$templateCard.addClass("selected"),l.setToolbarState(e),window.WPFormsChallenge?.core.freezeChallenge(e,r.misc.frozenChallengeTooltip),n("body").toggleClass("wpforms-ai-form-generator-active",e),!o.state.aiResponseHistory&&wpforms_ai_chat_element.forms.responseHistory&&(o.state.aiResponseHistory=wpforms_ai_chat_element.forms.responseHistory,t=(e=l.el.$chat.find(".wpforms-chat-item-answer.active")).data("response-id"),o.state.aiResponse=o.state.aiResponseHistory[t],e[0].scrollIntoView({behavior:"smooth",block:"end"}))},chatStart(e){e&&l.el.$templateCard.addClass("selected").find(".wpforms-template-generate").text(r.templateCard.buttonTextContinue)},aiResponse(e){e&&o.preview.update()},isPreviewUpdate(e){l.el.$chat.toggleClass("wpforms-ai-chat-inactive",e)}},render:{generatorPanel(){return`
|
||||
<div class="wpforms-panel wpforms-panel-fields" id="wpforms-panel-ai-form">
|
||||
<div class="wpforms-panel-sidebar-content">
|
||||
<div class="wpforms-panel-sidebar">
|
||||
<div class="wpforms-panel-sidebar-header">
|
||||
<button type="button" class="wpforms-btn-back-to-templates" aria-label="${r.panel.backToTemplates}">
|
||||
${r.panel.backToTemplates}
|
||||
</button>
|
||||
</div>
|
||||
<wpforms-ai-chat mode="forms" class="wpforms-ai-chat-blue"/>
|
||||
</div>
|
||||
<div class="wpforms-panel-content-wrap">
|
||||
<div class="wpforms-panel-content">
|
||||
<div class="wpforms-panel-empty-state">
|
||||
<h4>${r.panel.emptyStateTitle}</h4>
|
||||
<p>${r.panel.emptyStateDesc}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`}},maybeOpenPanel(){if(window.location.search.includes("&ai-form")){history.replaceState({},null,wpf.updateQueryString("ai-form",null));let e=n(".wpforms-template-generate.enable-lite-connect-modal");e.length?setTimeout(function(){e.trigger("click")},0):!Object.keys(r.addonsData).length||r.dismissed.installAddons?o.state.panelOpen=!0:o.modals.openAddonsModal(null)}},useFormAjax(e){var t=e.closest(".wpforms-ai-chat").data("session-id"),a=e.closest(".wpforms-chat-item").data("response-id");WPFormsBuilder.showLoadingOverlay(),l.getChatElement()?.wpformsAiApi.rate(!0,a),WPFormsBuilder.setCloseConfirmation(!1);let s={action:"wpforms_use_ai_form",nonce:r.nonce,formId:o.state.formId,formData:o.state.aiResponseHistory[a],sessionId:t,chatHtml:e.closest("wpforms-ai-chat").html(),responseHistory:o.state.aiResponseHistory};o.preview.closeTooltips(),n.post(r.ajaxUrl,s).done(function(t){if(t.success){let e=s.formId?"":"&newform=1";window.WPFormsChallenge?WPFormsChallenge.core.resumeChallengeAndExec({},()=>{WPFormsChallenge.core.stepCompleted(2).done(()=>{window.location.assign(t.data.redirect+e)})}):window.location.assign(t.data.redirect+e)}else wpf.debug("Form Generator AJAX error:",t.data.error??t.data)}).fail(function(e){wpf.debug("Form Generator AJAX error:",e.responseText??e.statusText)})},setToolbarState(e){l.el.$builderToolbar.toggleClass("empty",e),l.el.$builderToolbar.find(".js-wpforms-help span").toggleClass("screen-reader-text",!e)},getChatElement(){return l.el.$chat.parent()[0]}};return l}
|
||||
Executable
+389
@@ -0,0 +1,389 @@
|
||||
/* global wpforms_ai_form_generator, wpf, WPFormsBuilder, wpforms_builder */
|
||||
|
||||
/**
|
||||
* @param strings.addonsAction
|
||||
* @param strings.addonsData
|
||||
* @param strings.addons.installTitle
|
||||
* @param strings.addons.installContent
|
||||
* @param strings.addons.activateContent
|
||||
* @param strings.addons.installButton
|
||||
* @param strings.addons.installConfirmButton
|
||||
* @param strings.addons.activateConfirmButton
|
||||
* @param strings.addons.cancelButton
|
||||
* @param strings.addons.dontShow
|
||||
* @param strings.addons.dismissErrorTitle
|
||||
* @param strings.addons.dismissError
|
||||
* @param strings.addons.addonsInstalledTitle
|
||||
* @param strings.addons.addonsActivatedTitle
|
||||
* @param strings.addons.addonsInstalledContent
|
||||
* @param strings.addons.okay
|
||||
* @param strings.addons.addonsInstallErrorTitle
|
||||
* @param strings.addons.addonsActivateErrorTitle
|
||||
* @param strings.addons.addonsInstallError
|
||||
* @param strings.addons.addonsInstallErrorNetwork
|
||||
* @param strings.adminNonce
|
||||
* @param strings.misc.warningExistingForm
|
||||
* @param this.$$confirm
|
||||
* @param this.$$cancel
|
||||
*/
|
||||
|
||||
/**
|
||||
* The WPForms AI form generator app.
|
||||
*
|
||||
* Modal windows' module.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {Object} generator The AI form generator.
|
||||
* @param {Object} $ jQuery function.
|
||||
*
|
||||
* @return {Object} The preview module object.
|
||||
*/
|
||||
export default function( generator, $ ) { // eslint-disable-line max-lines-per-function
|
||||
/**
|
||||
* Localized strings.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const strings = wpforms_ai_form_generator;
|
||||
|
||||
/**
|
||||
* The preview module object.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
const modals = {
|
||||
/**
|
||||
* DOM elements.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
el: {},
|
||||
|
||||
/**
|
||||
* AJAX error debug string.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
ajaxError: 'Form Generator AJAX error:',
|
||||
|
||||
/**
|
||||
* Init generator.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
init() {
|
||||
modals.el.$doc = $( document );
|
||||
modals.el.$templateCard = $( '#wpforms-template-generate' );
|
||||
|
||||
modals.events();
|
||||
},
|
||||
|
||||
/**
|
||||
* Register events.
|
||||
*/
|
||||
events() {
|
||||
modals.el.$doc.on( 'change', '.wpforms-ai-forms-install-addons-modal-dismiss', modals.dismissAddonsModal );
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the addons modal.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {Object} e Event object.
|
||||
*/
|
||||
openAddonsModal( e ) { // eslint-disable-line max-lines-per-function
|
||||
e?.preventDefault();
|
||||
|
||||
const spinner = '<i class="wpforms-loading-spinner wpforms-loading-white wpforms-loading-inline"></i>';
|
||||
const isInstall = strings.addonsAction === 'install';
|
||||
const content = isInstall ? strings.addons.installContent : strings.addons.activateContent;
|
||||
|
||||
const options = {
|
||||
title: strings.addons.installTitle,
|
||||
content,
|
||||
type: 'purple',
|
||||
icon: 'fa fa-info-circle',
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: isInstall ? strings.addons.installConfirmButton : strings.addons.activateConfirmButton,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
action() {
|
||||
const label = isInstall ? strings.addons.installing : strings.addons.activating;
|
||||
|
||||
this.$$confirm.prop( 'disabled', true ).html( spinner + label );
|
||||
this.$$cancel.prop( 'disabled', true );
|
||||
|
||||
modals.installAddonsAjax( this );
|
||||
|
||||
return false;
|
||||
},
|
||||
},
|
||||
cancel: {
|
||||
text: strings.addons.cancelButton,
|
||||
keys: [ 'esc' ],
|
||||
btnClass: 'btn-cancel',
|
||||
action() {
|
||||
modals.updateGenerateFormButton( false );
|
||||
|
||||
// Open the Form Generator panel.
|
||||
setTimeout( () => {
|
||||
generator.state.panelOpen = true;
|
||||
}, 250 );
|
||||
},
|
||||
},
|
||||
},
|
||||
onOpenBefore() {
|
||||
// Add the checkbox to the modal.
|
||||
const dontShowAgain = `
|
||||
<label class="jconfirm-checkbox">
|
||||
<input type="checkbox" class="jconfirm-checkbox-input wpforms-ai-forms-install-addons-modal-dismiss">
|
||||
${ strings.addons.dontShow }
|
||||
</label>
|
||||
`;
|
||||
|
||||
this.$body
|
||||
.addClass( 'wpforms-ai-forms-install-addons-modal' )
|
||||
.find( '.jconfirm-buttons' )
|
||||
.after( dontShowAgain );
|
||||
},
|
||||
};
|
||||
|
||||
$.confirm( options );
|
||||
},
|
||||
|
||||
/**
|
||||
* Install required addons AJAX.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {Object} previousModal Previous modal instance.
|
||||
*/
|
||||
installAddonsAjax( previousModal ) { // eslint-disable-line max-lines-per-function
|
||||
let chain = null;
|
||||
let errorDisplayed = false;
|
||||
|
||||
const postDone = function( res ) {
|
||||
if ( ! res.success ) {
|
||||
wpf.debug( modals.ajaxError, res.data.error ?? res.data );
|
||||
}
|
||||
|
||||
if ( ! res.success && ! errorDisplayed ) {
|
||||
errorDisplayed = true;
|
||||
|
||||
modals.openErrorModal( {
|
||||
title: strings.addonsAction === 'install' ? strings.addons.addonsInstallErrorTitle : strings.addons.addonsActivateErrorTitle,
|
||||
content: strings.addons.addonsInstallError,
|
||||
} );
|
||||
}
|
||||
};
|
||||
|
||||
const postFail = function( xhr ) {
|
||||
if ( errorDisplayed ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const error = xhr.responseText || strings.addons.addonsInstallErrorNetwork;
|
||||
let content = strings.addons.addonsInstallError;
|
||||
|
||||
content += error && error !== 'error' ? '<br>' + error : '';
|
||||
|
||||
wpf.debug( modals.ajaxError, content );
|
||||
|
||||
modals.openErrorModal( {
|
||||
title: strings.addonsAction === 'install' ? strings.addons.addonsInstallErrorTitle : strings.addons.addonsActivateErrorTitle,
|
||||
content,
|
||||
} );
|
||||
|
||||
errorDisplayed = true;
|
||||
};
|
||||
|
||||
// Do not display the alert about unsaved changes.
|
||||
WPFormsBuilder.setCloseConfirmation( false );
|
||||
|
||||
// Loop through all addons and make a chained AJAX calls.
|
||||
for ( const slug in strings.addonsData ) {
|
||||
const url = strings.addonsData[ slug ]?.url;
|
||||
const data = {
|
||||
action: url ? 'wpforms_install_addon' : 'wpforms_activate_addon',
|
||||
nonce : strings.adminNonce,
|
||||
plugin: url ? url : strings.addonsData[ slug ]?.path,
|
||||
type : 'addon',
|
||||
};
|
||||
|
||||
if ( chain === null ) {
|
||||
chain = $.post( strings.ajaxUrl, data, postDone );
|
||||
} else {
|
||||
chain = chain.then( () => {
|
||||
return $.post( strings.ajaxUrl, data, postDone );
|
||||
} );
|
||||
}
|
||||
|
||||
chain.fail( postFail );
|
||||
}
|
||||
|
||||
// Open the Addons Installed modal after the last AJAX call.
|
||||
chain
|
||||
.then( () => {
|
||||
if ( ! errorDisplayed ) {
|
||||
modals.openAddonsInstalledModal();
|
||||
}
|
||||
} )
|
||||
.always( () => {
|
||||
previousModal.close();
|
||||
modals.updateGenerateFormButton( false );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Dismiss or de-dismiss element.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
dismissAddonsModal() {
|
||||
const $checkbox = $( this );
|
||||
const isChecked = $checkbox.prop( 'checked' );
|
||||
|
||||
const data = {
|
||||
action: 'wpforms_dismiss_ai_form',
|
||||
nonce: strings.nonce,
|
||||
element: 'install-addons-modal',
|
||||
dismiss: isChecked,
|
||||
};
|
||||
|
||||
modals.updateGenerateFormButton( ! isChecked );
|
||||
|
||||
$.post( strings.ajaxUrl, data )
|
||||
.done( function( res ) {
|
||||
if ( res.success ) {
|
||||
return;
|
||||
}
|
||||
|
||||
modals.openErrorModal( {
|
||||
title: strings.addons.dismissErrorTitle,
|
||||
content: strings.addons.dismissError,
|
||||
} );
|
||||
|
||||
wpf.debug( modals.ajaxError, res.data.error ?? res.data );
|
||||
} )
|
||||
.fail( function( xhr ) {
|
||||
modals.openErrorModal( {
|
||||
title: strings.addons.dismissErrorTitle,
|
||||
content: strings.addons.dismissError + '<br>' + strings.addons.addonsInstallErrorNetwork,
|
||||
} );
|
||||
|
||||
wpf.debug( modals.ajaxError, xhr.responseText ?? xhr.statusText );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the Generate Form button to enable/disable install addons modal window.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {boolean} shouldInstallAddons Should open install addons modal.
|
||||
*/
|
||||
updateGenerateFormButton( shouldInstallAddons ) {
|
||||
if ( shouldInstallAddons ) {
|
||||
$( '.wpforms-template-generate' )
|
||||
.removeClass( 'wpforms-template-generate' )
|
||||
.addClass( 'wpforms-template-generate-install-addons' );
|
||||
} else {
|
||||
$( '.wpforms-template-generate-install-addons' )
|
||||
.removeClass( 'wpforms-template-generate-install-addons' )
|
||||
.addClass( 'wpforms-template-generate' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the Addons Installed modal.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
openAddonsInstalledModal() {
|
||||
const options = {
|
||||
title: strings.addonsAction === 'install' ? strings.addons.addonsInstalledTitle : strings.addons.addonsActivatedTitle,
|
||||
content: strings.addons.addonsInstalledContent,
|
||||
icon: 'fa fa-check-circle',
|
||||
type: 'green',
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: strings.addons.okay,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
action() {
|
||||
WPFormsBuilder.showLoadingOverlay();
|
||||
window.location = window.location + '&ai-form';
|
||||
},
|
||||
},
|
||||
},
|
||||
onOpenBefore() {
|
||||
this.$body
|
||||
.addClass( 'wpforms-ai-forms-addons-installed-modal' );
|
||||
},
|
||||
};
|
||||
|
||||
$.confirm( options );
|
||||
},
|
||||
|
||||
/**
|
||||
* Warning for the existing form.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {jQuery} $button The "Use This Form" button.
|
||||
*/
|
||||
openExistingFormModal( $button ) {
|
||||
$.confirm( {
|
||||
title: wpforms_builder.heads_up,
|
||||
content: strings.misc.warningExistingForm,
|
||||
icon: 'fa fa-exclamation-circle',
|
||||
type: 'orange',
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: wpforms_builder.ok,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
action() {
|
||||
generator.main.useFormAjax( $button );
|
||||
},
|
||||
},
|
||||
cancel: {
|
||||
text: wpforms_builder.cancel,
|
||||
},
|
||||
},
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the error modal.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {Object} args Arguments.
|
||||
*/
|
||||
openErrorModal( args ) {
|
||||
const options = {
|
||||
title: args.title ?? false,
|
||||
content: args.content ?? false,
|
||||
icon: 'fa fa-exclamation-circle',
|
||||
type: 'red',
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: strings.addons.okay,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
$.confirm( options );
|
||||
},
|
||||
};
|
||||
|
||||
return modals;
|
||||
}
|
||||
Vendored
Executable
+6
@@ -0,0 +1,6 @@
|
||||
export default function(a,r){let l=wpforms_ai_form_generator,i={el:{},ajaxError:"Form Generator AJAX error:",init(){i.el.$doc=r(document),i.el.$templateCard=r("#wpforms-template-generate"),i.events()},events(){i.el.$doc.on("change",".wpforms-ai-forms-install-addons-modal-dismiss",i.dismissAddonsModal)},openAddonsModal(n){n?.preventDefault();let o="install"===l.addonsAction;n=o?l.addons.installContent:l.addons.activateContent,n={title:l.addons.installTitle,content:n,type:"purple",icon:"fa fa-info-circle",buttons:{confirm:{text:o?l.addons.installConfirmButton:l.addons.activateConfirmButton,btnClass:"btn-confirm",keys:["enter"],action(){var n=o?l.addons.installing:l.addons.activating;return this.$$confirm.prop("disabled",!0).html('<i class="wpforms-loading-spinner wpforms-loading-white wpforms-loading-inline"></i>'+n),this.$$cancel.prop("disabled",!0),i.installAddonsAjax(this),!1}},cancel:{text:l.addons.cancelButton,keys:["esc"],btnClass:"btn-cancel",action(){i.updateGenerateFormButton(!1),setTimeout(()=>{a.state.panelOpen=!0},250)}}},onOpenBefore(){var n=`
|
||||
<label class="jconfirm-checkbox">
|
||||
<input type="checkbox" class="jconfirm-checkbox-input wpforms-ai-forms-install-addons-modal-dismiss">
|
||||
${l.addons.dontShow}
|
||||
</label>
|
||||
`;this.$body.addClass("wpforms-ai-forms-install-addons-modal").find(".jconfirm-buttons").after(n)}};r.confirm(n)},installAddonsAjax(n){function o(n){n.success||wpf.debug(i.ajaxError,n.data.error??n.data),n.success||t||(t=!0,i.openErrorModal({title:"install"===l.addonsAction?l.addons.addonsInstallErrorTitle:l.addons.addonsActivateErrorTitle,content:l.addons.addonsInstallError}))}let a=null,t=!1;function e(n){var o;t||(n=n.responseText||l.addons.addonsInstallErrorNetwork,o=l.addons.addonsInstallError,o+=n&&"error"!==n?"<br>"+n:"",wpf.debug(i.ajaxError,o),i.openErrorModal({title:"install"===l.addonsAction?l.addons.addonsInstallErrorTitle:l.addons.addonsActivateErrorTitle,content:o}),t=!0)}for(var s in WPFormsBuilder.setCloseConfirmation(!1),l.addonsData){var d=l.addonsData[s]?.url;let n={action:d?"wpforms_install_addon":"wpforms_activate_addon",nonce:l.adminNonce,plugin:d||l.addonsData[s]?.path,type:"addon"};(a=null===a?r.post(l.ajaxUrl,n,o):a.then(()=>r.post(l.ajaxUrl,n,o))).fail(e)}a.then(()=>{t||i.openAddonsInstalledModal()}).always(()=>{n.close(),i.updateGenerateFormButton(!1)})},dismissAddonsModal(){var n=r(this).prop("checked"),o={action:"wpforms_dismiss_ai_form",nonce:l.nonce,element:"install-addons-modal",dismiss:n};i.updateGenerateFormButton(!n),r.post(l.ajaxUrl,o).done(function(n){n.success||(i.openErrorModal({title:l.addons.dismissErrorTitle,content:l.addons.dismissError}),wpf.debug(i.ajaxError,n.data.error??n.data))}).fail(function(n){i.openErrorModal({title:l.addons.dismissErrorTitle,content:l.addons.dismissError+"<br>"+l.addons.addonsInstallErrorNetwork}),wpf.debug(i.ajaxError,n.responseText??n.statusText)})},updateGenerateFormButton(n){n?r(".wpforms-template-generate").removeClass("wpforms-template-generate").addClass("wpforms-template-generate-install-addons"):r(".wpforms-template-generate-install-addons").removeClass("wpforms-template-generate-install-addons").addClass("wpforms-template-generate")},openAddonsInstalledModal(){var n={title:"install"===l.addonsAction?l.addons.addonsInstalledTitle:l.addons.addonsActivatedTitle,content:l.addons.addonsInstalledContent,icon:"fa fa-check-circle",type:"green",buttons:{confirm:{text:l.addons.okay,btnClass:"btn-confirm",keys:["enter"],action(){WPFormsBuilder.showLoadingOverlay(),window.location=window.location+"&ai-form"}}},onOpenBefore(){this.$body.addClass("wpforms-ai-forms-addons-installed-modal")}};r.confirm(n)},openExistingFormModal(n){r.confirm({title:wpforms_builder.heads_up,content:l.misc.warningExistingForm,icon:"fa fa-exclamation-circle",type:"orange",buttons:{confirm:{text:wpforms_builder.ok,btnClass:"btn-confirm",keys:["enter"],action(){a.main.useFormAjax(n)}},cancel:{text:wpforms_builder.cancel}}})},openErrorModal(n){n={title:n.title??!1,content:n.content??!1,icon:"fa fa-exclamation-circle",type:"red",buttons:{confirm:{text:l.addons.okay,btnClass:"btn-confirm",keys:["enter"]}}};r.confirm(n)}};return i}
|
||||
Executable
+410
@@ -0,0 +1,410 @@
|
||||
/* global wpforms_ai_form_generator, wpf, wpforms_addons */
|
||||
|
||||
/**
|
||||
* @param strings.dismissed.previewNotice
|
||||
* @param strings.licenseType
|
||||
* @param strings.previewNotice.btnUpgrade
|
||||
* @param strings.previewNotice.msgUpgrade
|
||||
* @param wpforms_ai_form_generator.addonFields
|
||||
*/
|
||||
|
||||
/**
|
||||
* The WPForms AI form generator app.
|
||||
*
|
||||
* Form preview module.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {Object} generator The AI form generator.
|
||||
* @param {Object} $ jQuery function.
|
||||
*
|
||||
* @return {Object} The preview module object.
|
||||
*/
|
||||
export default function( generator, $ ) { // eslint-disable-line max-lines-per-function
|
||||
/**
|
||||
* Localized strings.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const strings = wpforms_ai_form_generator;
|
||||
|
||||
/**
|
||||
* The preview module object.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
const preview = {
|
||||
/**
|
||||
* DOM elements.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
el: {},
|
||||
|
||||
/**
|
||||
* Mouse coordinates.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
mouse: {},
|
||||
|
||||
/**
|
||||
* Init module.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
init() {
|
||||
preview.el.$contentWrap = generator.main.el.$generatorPanel.find( '.wpforms-panel-content-wrap' );
|
||||
preview.el.$content = preview.el.$contentWrap.find( '.wpforms-panel-content' );
|
||||
preview.el.$emptyState = preview.el.$content.find( '.wpforms-panel-empty-state' );
|
||||
|
||||
preview.events();
|
||||
},
|
||||
|
||||
/**
|
||||
* Preview events.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
events() {
|
||||
// Track mouse coordinates.
|
||||
$( document ).on( 'mousemove', ( e ) => {
|
||||
preview.mouse.x = e.pageX;
|
||||
preview.mouse.y = e.pageY;
|
||||
} );
|
||||
|
||||
preview.el.$contentWrap.on( 'scroll', preview.closeTooltips );
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the preview according to the response stored in the generator state.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
update() { // eslint-disable-line complexity
|
||||
/**
|
||||
* @param response.fieldsOrder.length
|
||||
* @param response.settings.submit_text
|
||||
*/
|
||||
const response = generator.state.aiResponse;
|
||||
|
||||
if ( ! response || ! response.fields ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the preview update flag.
|
||||
generator.state.isPreviewUpdate = true;
|
||||
|
||||
// Reset preview fields. Here we will store the field ids that where added to the preview.
|
||||
generator.state.previewFields = [];
|
||||
|
||||
// Remove existing fields and hide empty state.
|
||||
preview.clear( false );
|
||||
|
||||
// Display the form header.
|
||||
preview.displayHeader( response );
|
||||
|
||||
for ( const key in response.fieldsOrder ) {
|
||||
const fieldId = response.fieldsOrder[ key ];
|
||||
preview.field( response.fields[ fieldId ], key );
|
||||
}
|
||||
|
||||
// Add submit button.
|
||||
if ( response.fieldsOrder?.length ) {
|
||||
preview.displaySubmit( response.settings?.submit_text || strings.panel.submitButton );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Show the empty state if there are no fields.
|
||||
preview.el.$emptyState.removeClass( 'wpforms-hidden-strict' );
|
||||
|
||||
generator.state.isPreviewUpdate = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* A single field preview.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {Object} fieldSettings Field settings.
|
||||
* @param {number} key Field key.
|
||||
*/
|
||||
async field( fieldSettings, key ) {
|
||||
// Add a field placeholder to the preview.
|
||||
const html = `
|
||||
<div id="wpforms-generator-field-${ fieldSettings.id ?? '' }" class="wpforms-ai-form-generator-preview-field">
|
||||
<div class="placeholder"></div>
|
||||
<div class="wpforms-field wpforms-field-${ fieldSettings.type ?? '' }"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
preview.el.$content.append( html );
|
||||
|
||||
const data = {
|
||||
action: 'wpforms_get_ai_form_field_preview',
|
||||
nonce: strings.nonce,
|
||||
field: fieldSettings,
|
||||
};
|
||||
|
||||
// Delay the AJAX request to simulate one-by-one field loading.
|
||||
await preview.delay( 300 * key );
|
||||
|
||||
// Field preview AJAX request.
|
||||
$.post( strings.ajaxUrl, data )
|
||||
.done( function( res ) {
|
||||
if ( ! res.success ) {
|
||||
wpf.debug( 'Form Generator AJAX error:', res.data.error ?? res.data );
|
||||
return;
|
||||
}
|
||||
|
||||
preview.displayField( res.data ?? '', fieldSettings );
|
||||
} )
|
||||
.fail( function( xhr ) {
|
||||
wpf.debug( 'Form Generator AJAX error:', xhr.responseText ?? xhr.statusText );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Display the field in his placeholder.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {string} fieldHtml Field HTML.
|
||||
* @param {Object} fieldSettings Field settings.
|
||||
*/
|
||||
displayField( fieldHtml, fieldSettings ) {
|
||||
if ( ! fieldSettings.id && fieldSettings.id !== 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const $fieldBlock = preview.el.$content.find( '#wpforms-generator-field-' + fieldSettings.id );
|
||||
const $field = $fieldBlock.find( '.wpforms-field' );
|
||||
const $placeholder = $fieldBlock.find( '.placeholder' );
|
||||
|
||||
$placeholder
|
||||
.addClass( 'fade-out' );
|
||||
|
||||
$field
|
||||
.html( fieldHtml ?? '' )
|
||||
.addClass( 'fade-in' )
|
||||
.toggleClass( 'wpforms-hidden', ! fieldHtml ) // Hide preview if the field is empty.
|
||||
.toggleClass( 'required', fieldSettings.required === '1' ) // Display the required field mark (asterisk) on the field label.
|
||||
.toggleClass( 'label_empty', ! fieldSettings.label ); // The field with an empty label.
|
||||
|
||||
preview.initTooltip( $field );
|
||||
preview.initPageBreak( $field, fieldSettings );
|
||||
|
||||
generator.state.previewFields.push( fieldSettings.id );
|
||||
|
||||
// Detect whether all the fields are loaded.
|
||||
if ( generator.state.previewFields.length !== Object.keys( generator.state.aiResponse?.fields ).length ) {
|
||||
return;
|
||||
}
|
||||
|
||||
generator.state.isPreviewUpdate = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get addons used in AI response.
|
||||
*
|
||||
* @since 1.9.4
|
||||
*
|
||||
* @return {string} Addons used in the response.
|
||||
*/
|
||||
getAddonsUsedInResponse() { // eslint-disable-line complexity
|
||||
const response = generator.state.aiResponse;
|
||||
|
||||
if ( ! response || ! response.fields ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const addons = [];
|
||||
|
||||
for ( const key in response.fields ) {
|
||||
const addon = wpforms_ai_form_generator.addonFields[ response.fields[ key ].type ];
|
||||
|
||||
if ( ! addon ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const addonName = wpforms_addons[ 'wpforms-' + addon ]?.title.replace( strings.addons.addon, '' ).trim();
|
||||
|
||||
if ( ! addonName || addons.includes( addonName ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
addons.push( addonName );
|
||||
}
|
||||
|
||||
if ( ! addons.length ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
let lastAddon = addons.pop();
|
||||
|
||||
lastAddon += ' ' + strings.addons.addon;
|
||||
|
||||
return addons.length ? addons.join( ', ' ) + ', ' + strings.addons.and + ' ' + lastAddon : lastAddon;
|
||||
},
|
||||
|
||||
/**
|
||||
* Init the page breaks.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {jQuery} $field Field jQuery object.
|
||||
* @param {Object} fieldSettings Field settings.
|
||||
*/
|
||||
initPageBreak( $field, fieldSettings ) {
|
||||
if ( fieldSettings.type === 'pagebreak' && ! [ 'top', 'bottom' ].includes( fieldSettings.position ) ) {
|
||||
$field.addClass( 'wpforms-pagebreak-normal' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Init the preview tooltip.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {jQuery} $field Field jQuery object.
|
||||
*/
|
||||
initTooltip( $field ) {
|
||||
const width = 260;
|
||||
const args = {
|
||||
content: strings.panel.tooltipTitle + '<br>' + strings.panel.tooltipText,
|
||||
trigger: 'manual',
|
||||
interactive: true,
|
||||
animationDuration: 100,
|
||||
delay: 0,
|
||||
side: [ 'top' ],
|
||||
contentAsHTML: true,
|
||||
functionPosition: ( instance, helper, position ) => {
|
||||
// Set the tooltip position based on the mouse coordinates.
|
||||
position.coord.top = preview.mouse.y - 57;
|
||||
position.coord.left = preview.mouse.x - ( width / 2 );
|
||||
|
||||
return position;
|
||||
},
|
||||
};
|
||||
|
||||
// Initialize.
|
||||
$field.tooltipster( args );
|
||||
preview.toggleTooltipOnClick( $field );
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggle the preview tooltip on click.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {jQuery} $field Field jQuery object.
|
||||
*/
|
||||
toggleTooltipOnClick( $field ) {
|
||||
$field.on( 'click', () => {
|
||||
// Close opened tooltips on other fields.
|
||||
preview.closeTooltips();
|
||||
|
||||
const status = $field.tooltipster( 'status' );
|
||||
|
||||
$field.tooltipster( status.state === 'closed' ? 'open' : 'close' );
|
||||
|
||||
if ( status.state !== 'closed' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const instance = $field.tooltipster( 'instance' );
|
||||
|
||||
// Adjust tooltip styling.
|
||||
instance._$tooltip.css( {
|
||||
height: 'auto',
|
||||
} );
|
||||
|
||||
instance._$tooltip.find( '.tooltipster-arrow' ).css( {
|
||||
left: '50%',
|
||||
} );
|
||||
|
||||
// Close the tooltip after 5 seconds.
|
||||
setTimeout( function() {
|
||||
preview.closeTooltips();
|
||||
}, 5000 );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Close tooltips.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*/
|
||||
closeTooltips() {
|
||||
preview.el.$content.find( '.wpforms-field' ).each( function() {
|
||||
const $this = $( this );
|
||||
|
||||
if ( $this.hasClass( 'tooltipstered' ) && $this.parent().length ) {
|
||||
$this.tooltipster( 'close' );
|
||||
}
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Display the form header.
|
||||
*
|
||||
* @since 1.9.4
|
||||
*
|
||||
* @param {Object} response Button text.
|
||||
*/
|
||||
displayHeader( response ) {
|
||||
const title = `<h2 class="wpforms-ai-form-generator-preview-title">${ response.form_title ?? '' }</h2>`;
|
||||
|
||||
// Add form title.
|
||||
preview.el.$content.prepend( title );
|
||||
},
|
||||
|
||||
/**
|
||||
* Display the `submit` button.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {string} label Button text.
|
||||
*/
|
||||
displaySubmit( label ) {
|
||||
preview.el.$content
|
||||
.append( `<button type="button" value="${ label }" class="wpforms-ai-form-generator-preview-submit">${ label }</button>` );
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear the preview content.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {boolean} isEmptyState Whether to show the empty state or not.
|
||||
*/
|
||||
clear( isEmptyState = true ) {
|
||||
preview.el.$content.find( '.wpforms-ai-form-generator-preview-field' ).remove();
|
||||
preview.el.$content.find( '.wpforms-ai-form-generator-preview-placeholder' ).remove();
|
||||
preview.el.$content.find( '.wpforms-ai-form-generator-preview-title' ).remove();
|
||||
preview.el.$content.find( '.wpforms-ai-form-generator-preview-addons-notice' ).remove();
|
||||
preview.el.$content.find( '.wpforms-ai-form-generator-preview-submit' ).remove();
|
||||
preview.el.$emptyState.toggleClass( 'wpforms-hidden-strict', ! isEmptyState );
|
||||
},
|
||||
|
||||
/**
|
||||
* Delay promise.
|
||||
*
|
||||
* @since 1.9.2
|
||||
*
|
||||
* @param {number} time Time in milliseconds.
|
||||
*
|
||||
* @return {Promise} Promise.
|
||||
*/
|
||||
delay( time ) {
|
||||
return new Promise( ( res ) => {
|
||||
setTimeout( res, time );
|
||||
} );
|
||||
},
|
||||
};
|
||||
|
||||
return preview;
|
||||
}
|
||||
Vendored
Executable
+6
@@ -0,0 +1,6 @@
|
||||
export default function(s,i){let n=wpforms_ai_form_generator,r={el:{},mouse:{},init(){r.el.$contentWrap=s.main.el.$generatorPanel.find(".wpforms-panel-content-wrap"),r.el.$content=r.el.$contentWrap.find(".wpforms-panel-content"),r.el.$emptyState=r.el.$content.find(".wpforms-panel-empty-state"),r.events()},events(){i(document).on("mousemove",e=>{r.mouse.x=e.pageX,r.mouse.y=e.pageY}),r.el.$contentWrap.on("scroll",r.closeTooltips)},update(){var e=s.state.aiResponse;if(e&&e.fields){for(var t in s.state.isPreviewUpdate=!0,s.state.previewFields=[],r.clear(!1),r.displayHeader(e),e.fieldsOrder){var o=e.fieldsOrder[t];r.field(e.fields[o],t)}e.fieldsOrder?.length?r.displaySubmit(e.settings?.submit_text||n.panel.submitButton):(r.el.$emptyState.removeClass("wpforms-hidden-strict"),s.state.isPreviewUpdate=!1)}},async field(t,e){var o=`
|
||||
<div id="wpforms-generator-field-${t.id??""}" class="wpforms-ai-form-generator-preview-field">
|
||||
<div class="placeholder"></div>
|
||||
<div class="wpforms-field wpforms-field-${t.type??""}"></div>
|
||||
</div>
|
||||
`,o=(r.el.$content.append(o),{action:"wpforms_get_ai_form_field_preview",nonce:n.nonce,field:t});await r.delay(300*e),i.post(n.ajaxUrl,o).done(function(e){e.success?r.displayField(e.data??"",t):wpf.debug("Form Generator AJAX error:",e.data.error??e.data)}).fail(function(e){wpf.debug("Form Generator AJAX error:",e.responseText??e.statusText)})},displayField(e,t){var o,i;!t.id&&0!==t.id||(i=(o=r.el.$content.find("#wpforms-generator-field-"+t.id)).find(".wpforms-field"),o.find(".placeholder").addClass("fade-out"),i.html(e??"").addClass("fade-in").toggleClass("wpforms-hidden",!e).toggleClass("required","1"===t.required).toggleClass("label_empty",!t.label),r.initTooltip(i),r.initPageBreak(i,t),s.state.previewFields.push(t.id),s.state.previewFields.length!==Object.keys(s.state.aiResponse?.fields).length)||(s.state.isPreviewUpdate=!1)},getAddonsUsedInResponse(){var e=s.state.aiResponse;if(!e||!e.fields)return"";var t,o,i=[];for(t in e.fields){var r=wpforms_ai_form_generator.addonFields[e.fields[t].type];r&&(r=wpforms_addons["wpforms-"+r]?.title.replace(n.addons.addon,"").trim())&&!i.includes(r)&&i.push(r)}return i.length?(o=i.pop(),o+=" "+n.addons.addon,i.length?i.join(", ")+", "+n.addons.and+" "+o:o):""},initPageBreak(e,t){"pagebreak"!==t.type||["top","bottom"].includes(t.position)||e.addClass("wpforms-pagebreak-normal")},initTooltip(e){var t={content:n.panel.tooltipTitle+"<br>"+n.panel.tooltipText,trigger:"manual",interactive:!0,animationDuration:100,delay:0,side:["top"],contentAsHTML:!0,functionPosition:(e,t,o)=>(o.coord.top=r.mouse.y-57,o.coord.left=r.mouse.x-130,o)};e.tooltipster(t),r.toggleTooltipOnClick(e)},toggleTooltipOnClick(t){t.on("click",()=>{r.closeTooltips();var e=t.tooltipster("status");t.tooltipster("closed"===e.state?"open":"close"),"closed"===e.state&&((e=t.tooltipster("instance"))._$tooltip.css({height:"auto"}),e._$tooltip.find(".tooltipster-arrow").css({left:"50%"}),setTimeout(function(){r.closeTooltips()},5e3))})},closeTooltips(){r.el.$content.find(".wpforms-field").each(function(){var e=i(this);e.hasClass("tooltipstered")&&e.parent().length&&e.tooltipster("close")})},displayHeader(e){e=`<h2 class="wpforms-ai-form-generator-preview-title">${e.form_title??""}</h2>`;r.el.$content.prepend(e)},displaySubmit(e){r.el.$content.append(`<button type="button" value="${e}" class="wpforms-ai-form-generator-preview-submit">${e}</button>`)},clear(e=!0){r.el.$content.find(".wpforms-ai-form-generator-preview-field").remove(),r.el.$content.find(".wpforms-ai-form-generator-preview-placeholder").remove(),r.el.$content.find(".wpforms-ai-form-generator-preview-title").remove(),r.el.$content.find(".wpforms-ai-form-generator-preview-addons-notice").remove(),r.el.$content.find(".wpforms-ai-form-generator-preview-submit").remove(),r.el.$emptyState.toggleClass("wpforms-hidden-strict",!e)},delay(t){return new Promise(e=>{setTimeout(e,t)})}};return r}
|
||||
+249
@@ -0,0 +1,249 @@
|
||||
/* global wpf, WPFormsBuilder, WPFormsConstantContactV3AuthVars */
|
||||
|
||||
/**
|
||||
* @param window.wpforms_admin
|
||||
* @param window.wpforms_builder
|
||||
* @param WPFormsConstantContactV3AuthVars.auth_url
|
||||
*/
|
||||
|
||||
/**
|
||||
* WPForms Constant Contact V3 Popup.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*/
|
||||
const WPFormsConstantContactV3Auth = window.WPFormsConstantContactV3Auth || ( function( document, window, $ ) {
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const app = {
|
||||
/**
|
||||
* Is the authorization window opened?
|
||||
*
|
||||
* @since 1.9.3
|
||||
*/
|
||||
isOpened : false,
|
||||
|
||||
/**
|
||||
* URL to listen for messages from the window.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*/
|
||||
listenURL: '',
|
||||
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*/
|
||||
init: () => {
|
||||
$( app.ready );
|
||||
},
|
||||
|
||||
/**
|
||||
* Document ready.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*/
|
||||
ready: () => {
|
||||
const redirectUri = new URL( WPFormsConstantContactV3AuthVars.auth_url ).searchParams.get( 'redirect_uri' );
|
||||
app.listenURL = new URL( redirectUri ).origin;
|
||||
|
||||
$( document )
|
||||
.on( 'click', '.wpforms-constant-contact-v3-auth, .wpforms-builder-constant-contact-v3-provider-sign-up', app.showWindow )
|
||||
.on( 'click', '#wpforms-settings-constant-contact-v3-migration-prompt-link', app.promptMigration );
|
||||
},
|
||||
|
||||
/**
|
||||
* Show a window.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @param {Event} e Click event.
|
||||
*/
|
||||
showWindow: ( e ) => {
|
||||
e.preventDefault();
|
||||
|
||||
if ( app.isOpened ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const authUrl = WPFormsConstantContactV3AuthVars.auth_url,
|
||||
width = 500,
|
||||
height = 600,
|
||||
left = ( screen.width / 2 ) - ( width / 2 ),
|
||||
top = ( screen.height / 2 ) - ( height / 2 ),
|
||||
loginHintEmail = $( '.wpforms-constant-contact-v3-auth' ).data( 'login-hint' ),
|
||||
url = new URL( authUrl );
|
||||
|
||||
if ( loginHintEmail ) {
|
||||
url.searchParams.set( 'login_hint', loginHintEmail );
|
||||
}
|
||||
|
||||
const newWindow = window.open(
|
||||
url.toString(),
|
||||
'authPopup',
|
||||
'width=' + width + ', height=' + height + ', top=' + top + ', left=' + left
|
||||
);
|
||||
|
||||
window.addEventListener( 'message', app.listenResponse );
|
||||
const checkWindowClosed = setInterval( () => {
|
||||
if ( newWindow.closed ) {
|
||||
clearInterval( checkWindowClosed );
|
||||
app.isOpened = false;
|
||||
}
|
||||
}, 1000 );
|
||||
|
||||
app.isOpened = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Listen for response.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @param {Event} event Message event.
|
||||
*/
|
||||
listenResponse: ( event ) => {
|
||||
if ( event.origin !== app.listenURL ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! event.data ) {
|
||||
app.errorModal( WPFormsConstantContactV3AuthVars.strings.error );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
app.saveAccount( event.data );
|
||||
},
|
||||
|
||||
/**
|
||||
* Save account.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @param {string} code Authorization code.
|
||||
*/
|
||||
saveAccount: ( code ) => {
|
||||
const modal = app.waitModal();
|
||||
|
||||
$.post(
|
||||
WPFormsConstantContactV3AuthVars.ajax_url,
|
||||
{
|
||||
action: 'wpforms_constant_contact_popup_auth',
|
||||
data: JSON.stringify( { code } ),
|
||||
nonce: WPFormsConstantContactV3AuthVars.nonce,
|
||||
}
|
||||
)
|
||||
.done( ( response ) => {
|
||||
if ( ! response.success ) {
|
||||
modal.close();
|
||||
|
||||
const errorMessage =
|
||||
'<p>' + WPFormsConstantContactV3AuthVars.strings.error + '</p><p><strong>' + wpf.sanitizeHTML( response.data ) + '</strong></p>';
|
||||
|
||||
app.errorModal( errorMessage );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ( typeof WPFormsBuilder === 'undefined' ) {
|
||||
modal.close();
|
||||
window.location.href = WPFormsConstantContactV3AuthVars.page_url;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
WPFormsBuilder.formSave( false ).done( () => {
|
||||
WPFormsBuilder.setCloseConfirmation( false );
|
||||
WPFormsBuilder.showLoadingOverlay();
|
||||
location.reload();
|
||||
} );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Show a waiting modal.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @return {Object} Modal object.
|
||||
*/
|
||||
waitModal: () => {
|
||||
return $.alert( {
|
||||
title: '',
|
||||
content: WPFormsConstantContactV3AuthVars.strings.wait,
|
||||
icon: 'fa fa-info-circle',
|
||||
type: 'blue',
|
||||
buttons: false,
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Show an error modal.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @param {string} content Alert text.
|
||||
*
|
||||
* @return {Object} Modal object.
|
||||
*/
|
||||
errorModal: ( content ) => {
|
||||
const strings = window?.wpforms_builder || window?.wpforms_admin;
|
||||
|
||||
return $.alert( {
|
||||
title: strings.uh_oh,
|
||||
content,
|
||||
icon: 'fa fa-exclamation-circle',
|
||||
type: 'red',
|
||||
buttons: {
|
||||
cancel: {
|
||||
text: strings.cancel,
|
||||
action: () => {
|
||||
app.isOpened = false;
|
||||
},
|
||||
},
|
||||
},
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Prompt and start migration from v2 to v3 in the notice.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @param {Object} e Event object.
|
||||
*/
|
||||
promptMigration( e ) {
|
||||
e.preventDefault();
|
||||
|
||||
const modal = app.waitModal();
|
||||
|
||||
$.post( {
|
||||
url: WPFormsConstantContactV3AuthVars.ajax_url,
|
||||
data: {
|
||||
action: 'wpforms_constant_contact_migration_prompt',
|
||||
nonce: WPFormsConstantContactV3AuthVars.nonce,
|
||||
},
|
||||
success: () => {
|
||||
modal.close();
|
||||
window.location.href = WPFormsConstantContactV3AuthVars.page_url;
|
||||
},
|
||||
error: () => {
|
||||
modal.close();
|
||||
app.errorModal( WPFormsConstantContactV3AuthVars.strings.error );
|
||||
},
|
||||
} );
|
||||
},
|
||||
};
|
||||
|
||||
// Provide access to public functions/properties.
|
||||
return app;
|
||||
}( document, window, jQuery ) );
|
||||
|
||||
// Initialize.
|
||||
WPFormsConstantContactV3Auth.init();
|
||||
Vendored
Executable
+1
@@ -0,0 +1 @@
|
||||
let WPFormsConstantContactV3Auth=window.WPFormsConstantContactV3Auth||((o,s,i)=>{let c={isOpened:!1,listenURL:"",init:()=>{i(c.ready)},ready:()=>{var t=new URL(WPFormsConstantContactV3AuthVars.auth_url).searchParams.get("redirect_uri");c.listenURL=new URL(t).origin,i(o).on("click",".wpforms-constant-contact-v3-auth, .wpforms-builder-constant-contact-v3-provider-sign-up",c.showWindow).on("click","#wpforms-settings-constant-contact-v3-migration-prompt-link",c.promptMigration)},showWindow:n=>{if(n.preventDefault(),!c.isOpened){var n=WPFormsConstantContactV3AuthVars.auth_url,a=screen.width/2-250,r=screen.height/2-300,e=i(".wpforms-constant-contact-v3-auth").data("login-hint"),n=new URL(n);e&&n.searchParams.set("login_hint",e);let t=s.open(n.toString(),"authPopup","width=500, height=600, top="+r+", left="+a),o=(s.addEventListener("message",c.listenResponse),setInterval(()=>{t.closed&&(clearInterval(o),c.isOpened=!1)},1e3));c.isOpened=!0}},listenResponse:t=>{t.origin===c.listenURL&&(t.data?c.saveAccount(t.data):c.errorModal(WPFormsConstantContactV3AuthVars.strings.error))},saveAccount:t=>{let o=c.waitModal();i.post(WPFormsConstantContactV3AuthVars.ajax_url,{action:"wpforms_constant_contact_popup_auth",data:JSON.stringify({code:t}),nonce:WPFormsConstantContactV3AuthVars.nonce}).done(t=>{t.success?"undefined"==typeof WPFormsBuilder?(o.close(),s.location.href=WPFormsConstantContactV3AuthVars.page_url):WPFormsBuilder.formSave(!1).done(()=>{WPFormsBuilder.setCloseConfirmation(!1),WPFormsBuilder.showLoadingOverlay(),location.reload()}):(o.close(),t="<p>"+WPFormsConstantContactV3AuthVars.strings.error+"</p><p><strong>"+wpf.sanitizeHTML(t.data)+"</strong></p>",c.errorModal(t))})},waitModal:()=>i.alert({title:"",content:WPFormsConstantContactV3AuthVars.strings.wait,icon:"fa fa-info-circle",type:"blue",buttons:!1}),errorModal:t=>{var o=s?.wpforms_builder||s?.wpforms_admin;return i.alert({title:o.uh_oh,content:t,icon:"fa fa-exclamation-circle",type:"red",buttons:{cancel:{text:o.cancel,action:()=>{c.isOpened=!1}}}})},promptMigration(t){t.preventDefault();let o=c.waitModal();i.post({url:WPFormsConstantContactV3AuthVars.ajax_url,data:{action:"wpforms_constant_contact_migration_prompt",nonce:WPFormsConstantContactV3AuthVars.nonce},success:()=>{o.close(),s.location.href=WPFormsConstantContactV3AuthVars.page_url},error:()=>{o.close(),c.errorModal(WPFormsConstantContactV3AuthVars.strings.error)}})}};return c})(document,window,jQuery);WPFormsConstantContactV3Auth.init();
|
||||
+603
@@ -0,0 +1,603 @@
|
||||
/* global WPForms, wpf */
|
||||
|
||||
/**
|
||||
* WPForms Providers Builder ConstantContactV3 module.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*/
|
||||
WPForms.Admin.Builder.Providers.ConstantContactV3 = WPForms.Admin.Builder.Providers.ConstantContactV3 || ( function( document, window, $ ) {
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const app = {
|
||||
/**
|
||||
* CSS selectors.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
selectors: {
|
||||
accountField: '.js-wpforms-builder-constant-contact-v3-provider-connection-account',
|
||||
actionData: '.wpforms-builder-constant-contact-v3-provider-actions-data',
|
||||
actionField: '.js-wpforms-builder-constant-contact-v3-provider-connection-action',
|
||||
connection: '.wpforms-panel-content-section-constant-contact-v3 .wpforms-builder-provider-connection',
|
||||
},
|
||||
|
||||
/**
|
||||
* jQuery elements.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
$elements: {
|
||||
$connections: $( '.wpforms-panel-content-section-constant-contact-v3 .wpforms-builder-provider-connections' ),
|
||||
$holder: $( '#wpforms-panel-providers' ),
|
||||
$panel: $( '#constant-contact-v3-provider' ),
|
||||
},
|
||||
|
||||
/**
|
||||
* Current provider slug.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
provider: 'constant-contact-v3',
|
||||
|
||||
/**
|
||||
* This is a shortcut to the WPForms.Admin.Builder.Providers object,
|
||||
* that handles the parent all-providers functionality.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
Providers: {},
|
||||
|
||||
/**
|
||||
* This is a shortcut to the WPForms.Admin.Builder.Templates object,
|
||||
* that handles all the template management.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
Templates: {},
|
||||
|
||||
/**
|
||||
* This is a shortcut to the WPForms.Admin.Builder.Providers.cache object,
|
||||
* that handles all the cache management.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
Cache: {},
|
||||
|
||||
/**
|
||||
* This is a flag for ready state.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
isReady: false,
|
||||
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* Run initialization on the providers panel only.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*/
|
||||
init() {
|
||||
// We are requesting/loading a Providers panel.
|
||||
if ( wpf.getQueryString( 'view' ) === 'providers' ) {
|
||||
app.$elements.$holder.on( 'WPForms.Admin.Builder.Providers.ready', app.ready );
|
||||
}
|
||||
|
||||
// We have switched to a Providers panel.
|
||||
$( document ).on( 'wpformsPanelSwitched', function( event, panel ) {
|
||||
if ( panel === 'providers' ) {
|
||||
app.ready();
|
||||
}
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialized once the DOM and Providers are fully loaded.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*/
|
||||
ready() {
|
||||
if ( app.isReady ) {
|
||||
return;
|
||||
}
|
||||
|
||||
app.Providers = WPForms.Admin.Builder.Providers;
|
||||
app.Templates = WPForms.Admin.Builder.Templates;
|
||||
app.Cache = app.Providers.cache;
|
||||
|
||||
// Register custom Underscore.js templates.
|
||||
app.Templates.add( [
|
||||
'wpforms-constant-contact-v3-builder-content-connection',
|
||||
'wpforms-constant-contact-v3-builder-content-connection-error',
|
||||
'wpforms-constant-contact-v3-builder-content-connection-select-field',
|
||||
'wpforms-constant-contact-v3-builder-content-connection-conditionals',
|
||||
] );
|
||||
|
||||
// Events registration.
|
||||
app.bindUIActions();
|
||||
app.bindTriggers();
|
||||
|
||||
app.processInitial();
|
||||
|
||||
// Save a flag for ready state.
|
||||
app.isReady = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Process various events as a response to UI interactions.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*/
|
||||
bindUIActions() {
|
||||
app.$elements.$panel
|
||||
.on( 'connectionCreate', app.connection.create )
|
||||
.on( 'connectionDelete', app.connection.delete )
|
||||
.on( 'change', app.selectors.accountField, app.ui.accountField.change )
|
||||
.on( 'change', app.selectors.actionField, app.ui.actionField.change );
|
||||
},
|
||||
|
||||
/**
|
||||
* Fire certain events on certain actions, specific for related connections.
|
||||
* These are not directly caused by user manipulations.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*/
|
||||
bindTriggers() {
|
||||
app.$elements.$connections.on( 'connectionsDataLoaded', function( event, data ) {
|
||||
if ( _.isEmpty( data.connections ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
for ( const connectionId in data.connections ) {
|
||||
app.connection.generate( {
|
||||
connection: data.connections[ connectionId ],
|
||||
conditional: data.conditionals[ connectionId ],
|
||||
} );
|
||||
}
|
||||
} );
|
||||
|
||||
app.$elements.$connections.on( 'connectionGenerated', function( event, data ) {
|
||||
const $connection = app.connection.getById( data.connection.id );
|
||||
|
||||
if ( _.has( data.connection, 'isNew' ) && data.connection.isNew ) {
|
||||
// Run replacing temporary connection ID if it's a new connection.
|
||||
app.connection.replaceIds( data.connection.id, $connection );
|
||||
return;
|
||||
}
|
||||
|
||||
$( app.selectors.actionField, $connection ).trigger( 'change' );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Compile template with data if any and display them on a page.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*/
|
||||
processInitial() {
|
||||
app.connection.dataLoad();
|
||||
},
|
||||
|
||||
/**
|
||||
* Connection property.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*/
|
||||
connection: {
|
||||
/**
|
||||
* Sometimes we might need to a get a connection DOM element by its ID.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @param {string} connectionId Connection ID to search for a DOM element by.
|
||||
*
|
||||
* @return {jQuery} jQuery object for connection.
|
||||
*/
|
||||
getById( connectionId ) {
|
||||
return app.$elements.$connections.find( '.wpforms-builder-provider-connection[data-connection_id="' + connectionId + '"]' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Sometimes in DOM we might have placeholders or temporary connection IDs.
|
||||
* We need to replace them with actual values.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @param {string} connectionId New connection ID to replace to.
|
||||
* @param {Object} $connection jQuery DOM connection element.
|
||||
*/
|
||||
replaceIds( connectionId, $connection ) {
|
||||
// Replace old temporary %connection_id% from PHP code with the new one.
|
||||
$connection.find( 'input, select, label' ).each( function() {
|
||||
const $this = $( this );
|
||||
|
||||
if ( $this.attr( 'name' ) ) {
|
||||
$this.attr( 'name', $this.attr( 'name' ).replace( /%connection_id%/gi, connectionId ) );
|
||||
}
|
||||
|
||||
if ( $this.attr( 'id' ) ) {
|
||||
$this.attr( 'id', $this.attr( 'id' ).replace( /%connection_id%/gi, connectionId ) );
|
||||
}
|
||||
|
||||
if ( $this.attr( 'for' ) ) {
|
||||
$this.attr( 'for', $this.attr( 'for' ).replace( /%connection_id%/gi, connectionId ) );
|
||||
}
|
||||
|
||||
if ( $this.attr( 'data-name' ) ) {
|
||||
$this.attr( 'data-name', $this.attr( 'data-name' ).replace( /%connection_id%/gi, connectionId ) );
|
||||
}
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a connection using the user entered name.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @param {Object} event Event object.
|
||||
* @param {string} name Connection name.
|
||||
*/
|
||||
create( event, name ) {
|
||||
const connectionId = new Date().getTime().toString( 16 ),
|
||||
connection = {
|
||||
id: connectionId,
|
||||
name,
|
||||
isNew: true,
|
||||
};
|
||||
|
||||
app.Cache.addTo( app.provider, 'connections', connectionId, connection );
|
||||
|
||||
app.connection.generate( {
|
||||
connection,
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Connection is deleted - delete a cache as well.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @param {Object} event Event object.
|
||||
* @param {Object} $connection jQuery DOM element for a connection.
|
||||
*/
|
||||
delete( event, $connection ) {
|
||||
const $holder = app.Providers.getProviderHolder( app.provider );
|
||||
|
||||
if ( ! $connection.closest( $holder ).length ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const connectionId = $connection.data( 'connection_id' );
|
||||
|
||||
if ( _.isString( connectionId ) ) {
|
||||
app.Cache.deleteFrom( app.provider, 'connections', connectionId );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the template and data for a connection and process it.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @param {Object} data Connection data.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
generate( data ) {
|
||||
const accounts = app.Cache.get( app.provider, 'accounts' );
|
||||
|
||||
if ( _.isEmpty( accounts ) || ! app.account.isAccountExists( data.connection.account_id, accounts ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const actions = app.Cache.get( app.provider, 'actions' ),
|
||||
lists = app.Cache.get( app.provider, 'lists' );
|
||||
|
||||
return app.connection.renderConnections( accounts, lists, actions, data );
|
||||
},
|
||||
|
||||
/**
|
||||
* Render connections.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @param {Object} accounts List of accounts.
|
||||
* @param {Object} lists List of lists.
|
||||
* @param {Object} actions List of actions.
|
||||
* @param {Object} data Connection data.
|
||||
*/
|
||||
renderConnections( accounts, lists, actions, data ) {
|
||||
if ( ! app.account.isAccountExists( data.connection.account_id, accounts ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tmplConnection = app.Templates.get( 'wpforms-' + app.provider + '-builder-content-connection' ),
|
||||
tmplConditional = app.Templates.get( 'wpforms-constant-contact-v3-builder-content-connection-conditionals' ),
|
||||
conditional = _.has( data.connection, 'isNew' ) && data.connection.isNew ? tmplConditional() : data.conditional;
|
||||
|
||||
app.$elements.$connections.prepend(
|
||||
tmplConnection( {
|
||||
accounts,
|
||||
lists,
|
||||
actions,
|
||||
connection: data.connection,
|
||||
conditional,
|
||||
provider: app.provider,
|
||||
} )
|
||||
);
|
||||
|
||||
app.$elements.$connections.trigger( 'connectionGenerated', [ data ] );
|
||||
},
|
||||
|
||||
/**
|
||||
* Fire AJAX-request to retrieve the list of all saved connections.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*/
|
||||
dataLoad() {
|
||||
app
|
||||
.Providers.ajax
|
||||
.request( app.provider, {
|
||||
data: {
|
||||
task: 'connections_get',
|
||||
},
|
||||
} )
|
||||
.done( function( response ) {
|
||||
if (
|
||||
! response.success ||
|
||||
! _.has( response.data, 'connections' )
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
[
|
||||
'accounts',
|
||||
'actions',
|
||||
'actions_fields',
|
||||
'conditionals',
|
||||
'connections',
|
||||
'custom_fields',
|
||||
'lists',
|
||||
].forEach( ( dataType ) => {
|
||||
app.Cache.set( app.provider, dataType, jQuery.extend( {}, response.data[ dataType ] ) );
|
||||
} );
|
||||
|
||||
app.$elements.$connections.trigger( 'connectionsDataLoaded', [ response.data ] );
|
||||
} );
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Account property.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*/
|
||||
account: {
|
||||
/**
|
||||
* Check if a provided account is listed inside an account list.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @param {string} accountId Connection account ID to check.
|
||||
* @param {Object} accounts Array of objects, usually received from API.
|
||||
*
|
||||
* @return {boolean} True if an account exists.
|
||||
*/
|
||||
isAccountExists( accountId, accounts ) {
|
||||
if ( _.isEmpty( accounts ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// New connections that have not been saved don't have the account ID yet.
|
||||
if ( _.isEmpty( accountId ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return _.has( accounts, accountId );
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* All methods that modify the UI of a page.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*/
|
||||
ui: {
|
||||
/**
|
||||
* Account field methods.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*/
|
||||
accountField: {
|
||||
/**
|
||||
* Callback-function on change event.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*/
|
||||
change() {
|
||||
const $this = $( this ),
|
||||
$connection = $this.closest( app.selectors.connection ),
|
||||
$actionName = $( app.selectors.actionField, $connection );
|
||||
|
||||
$actionName.prop( 'selectedIndex', 0 ).trigger( 'change' );
|
||||
|
||||
// If an account is empty.
|
||||
if ( _.isEmpty( $this.val() ) ) {
|
||||
$actionName.prop( 'disabled', true );
|
||||
$( app.selectors.actionData, $connection ).html( '' );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$actionName.prop( 'disabled', false );
|
||||
$this.removeClass( 'wpforms-error' );
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Action methods.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*/
|
||||
actionField: {
|
||||
/**
|
||||
* Callback-function on change event.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*/
|
||||
change() {
|
||||
const $this = $( this ),
|
||||
$connection = $this.closest( app.selectors.connection ),
|
||||
$account = $( app.selectors.accountField, $connection ),
|
||||
$action = $( app.selectors.actionField, $connection );
|
||||
|
||||
app.ui.actionField.render( {
|
||||
action: 'action',
|
||||
target: $this,
|
||||
/* eslint-disable camelcase */
|
||||
account_id: $account.val(),
|
||||
action_name: $action.val(),
|
||||
connection_id: $connection.data( 'connection_id' ),
|
||||
/* eslint-enable camelcase */
|
||||
} );
|
||||
|
||||
$this.removeClass( 'wpforms-error' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Render HTML.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @param {Object} args Arguments.
|
||||
*/
|
||||
render( args ) {
|
||||
const fields = app.tmpl.renderActionFields( args ),
|
||||
$connection = app.connection.getById( args.connection_id ),
|
||||
$connectionData = $( app.selectors.actionData, $connection );
|
||||
|
||||
$connectionData.html( fields );
|
||||
|
||||
app.$elements.$holder.trigger( 'connectionRendered', [ app.provider, args.connection_id ] );
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a list of constant-contact lists.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @param {string} accountId Account ID.
|
||||
*
|
||||
* @return {Array} List of constant-contact lists.
|
||||
*/
|
||||
getList( accountId ) {
|
||||
const listsCache = app.Cache.get( app.provider, 'lists' );
|
||||
|
||||
return ! _.isEmpty( listsCache ) && ! _.isEmpty( listsCache[ accountId ] ) ? listsCache[ accountId ] : [];
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* All methods for JavaScript templates.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*/
|
||||
tmpl: {
|
||||
/**
|
||||
* Compile and retrieve an HTML for common elements.
|
||||
*
|
||||
* @since 1.9.3
|
||||
* @deprecated 1.9.5
|
||||
*
|
||||
* @return {string} Compiled HTML.
|
||||
*/
|
||||
commonsHTML() {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn( 'WARNING! Function "WPForms.Admin.Builder.Providers.ConstantContactV3.tmpl.commonsHTML()" has been deprecated!' );
|
||||
|
||||
const tmplError = app.Templates.get( 'wpforms-' + app.provider + '-builder-content-connection-error' );
|
||||
|
||||
return tmplError();
|
||||
},
|
||||
|
||||
/**
|
||||
* Compile and retrieve an HTML for "Custom Fields Table".
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @param {Object} args Arguments
|
||||
*
|
||||
* @return {string} Compiled HTML.
|
||||
*/
|
||||
renderActionFields( args ) {
|
||||
const fields = wpf.getFields(),
|
||||
actionsFields = app.Cache.get( app.provider, 'actions_fields' ),
|
||||
customFields = app.Cache.get( app.provider, 'custom_fields' ),
|
||||
connection = app.Cache.getById( app.provider, 'connections', args.connection_id );
|
||||
|
||||
let fieldHTML = '';
|
||||
|
||||
$.each( actionsFields[ args.target.val() ], function( key, field ) {
|
||||
if ( key === 'custom_fields' ) {
|
||||
const tmplFields = app.Templates.get( 'wpforms-providers-builder-content-connection-fields' );
|
||||
|
||||
fieldHTML += tmplFields( {
|
||||
connection,
|
||||
fields,
|
||||
provider: {
|
||||
slug: app.provider,
|
||||
fields: customFields[ args.account_id ],
|
||||
},
|
||||
isSupportSubfields: true,
|
||||
} );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const options = key === 'list' ? app.ui.actionField.getList( args.account_id ) : Object.values( fields );
|
||||
const templateName = 'wpforms-' + app.provider + '-builder-content-connection-' + field.type + '-field';
|
||||
const tmplField = app.Templates.get( templateName );
|
||||
|
||||
fieldHTML += tmplField( {
|
||||
connection,
|
||||
name: key,
|
||||
field,
|
||||
provider: {
|
||||
slug: app.provider,
|
||||
fields: actionsFields[ args.target.val() ],
|
||||
},
|
||||
options,
|
||||
} );
|
||||
} );
|
||||
|
||||
return fieldHTML;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Provide access to public functions/properties.
|
||||
return app;
|
||||
}( document, window, jQuery ) );
|
||||
|
||||
// Initialize.
|
||||
WPForms.Admin.Builder.Providers.ConstantContactV3.init();
|
||||
Vendored
Executable
+1
File diff suppressed because one or more lines are too long
+4195
File diff suppressed because one or more lines are too long
Vendored
Executable
+1
File diff suppressed because one or more lines are too long
+202
@@ -0,0 +1,202 @@
|
||||
/* global wpforms_divi_builder, WPFormsRepeaterField, ETBuilderBackendDynamic */
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
|
||||
/**
|
||||
* WPFormsSelector component.
|
||||
*
|
||||
* @since 1.6.3
|
||||
*/
|
||||
class WPFormsSelector extends Component {
|
||||
|
||||
/**
|
||||
* Module slug.
|
||||
*
|
||||
* @since 1.6.3
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
static slug = 'wpforms_selector';
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.6.3
|
||||
*
|
||||
* @param {string} props List of properties.
|
||||
*/
|
||||
constructor( props ) {
|
||||
|
||||
super( props );
|
||||
|
||||
this.state = {
|
||||
error: null,
|
||||
isLoading: true,
|
||||
form: null,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set types for properties.
|
||||
*
|
||||
* @since 1.6.3
|
||||
*
|
||||
* @returns {object} Properties type.
|
||||
*/
|
||||
static get propTypes() {
|
||||
|
||||
return {
|
||||
form_id: PropTypes.number, // eslint-disable-line camelcase
|
||||
show_title: PropTypes.string, // eslint-disable-line camelcase
|
||||
show_desc: PropTypes.string, // eslint-disable-line camelcase
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if form settings was updated.
|
||||
*
|
||||
* @since 1.6.3
|
||||
*
|
||||
* @param {object} prevProps List of previous properties.
|
||||
*/
|
||||
componentDidUpdate( prevProps ) {
|
||||
|
||||
if ( prevProps.form_id !== this.props.form_id || prevProps.show_title !== this.props.show_title || prevProps.show_desc !== this.props.show_desc ) {
|
||||
this.componentDidMount();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax request for form HTML.
|
||||
*
|
||||
* @since 1.6.3
|
||||
*/
|
||||
componentDidMount() {
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append( 'nonce', wpforms_divi_builder.nonce );
|
||||
formData.append( 'action', 'wpforms_divi_preview' );
|
||||
formData.append( 'form_id', this.props.form_id );
|
||||
formData.append( 'show_title', this.props.show_title );
|
||||
formData.append( 'show_desc', this.props.show_desc );
|
||||
|
||||
fetch(
|
||||
wpforms_divi_builder.ajax_url,
|
||||
{
|
||||
method: 'POST',
|
||||
cache: 'no-cache',
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Cache-Control': 'no-cache',
|
||||
},
|
||||
body: new URLSearchParams( formData ),
|
||||
},
|
||||
)
|
||||
.then( ( res ) => res.json() )
|
||||
.then(
|
||||
( result ) => {
|
||||
this.setState( {
|
||||
isLoading: false,
|
||||
form: result.data,
|
||||
} );
|
||||
},
|
||||
( error ) => {
|
||||
this.setState( {
|
||||
isLoading: false,
|
||||
error,
|
||||
} );
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render module view.
|
||||
*
|
||||
* @since 1.6.3
|
||||
*
|
||||
* @returns {JSX.Element} View for module.
|
||||
*/
|
||||
render() {
|
||||
const { error, isLoaded, form } = this.state,
|
||||
wrapperClasses = isLoaded ? 'wpforms-divi-form-preview loading' : 'wpforms-divi-form-preview';
|
||||
|
||||
if ( typeof this.props.form_id === 'undefined' || this.props.form_id === '' ) {
|
||||
return (
|
||||
<div className="wpforms-divi-empty-block">
|
||||
<img src={ wpforms_divi_builder.block_empty_url } alt="" />
|
||||
|
||||
{ <p dangerouslySetInnerHTML={ { __html: wpforms_divi_builder.block_empty_text } } /> }
|
||||
|
||||
<button type="button" onClick={
|
||||
() => {
|
||||
window.open( wpforms_divi_builder.get_started_url, '_blank' );
|
||||
}
|
||||
}
|
||||
>
|
||||
{ wpforms_divi_builder.get_started_text }
|
||||
</button>
|
||||
|
||||
<p className="wpforms-admin-no-forms-footer">
|
||||
{ wpforms_divi_builder.help_text }
|
||||
<a href={ wpforms_divi_builder.guide_url } onClick={
|
||||
() => {
|
||||
window.open( wpforms_divi_builder.guide_url, '_blank' );
|
||||
}
|
||||
}
|
||||
>
|
||||
{ wpforms_divi_builder.guide_text }.
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if ( error || ! form ) {
|
||||
return (
|
||||
<div className="wpforms-divi-form-placeholder">
|
||||
<img src={ wpforms_divi_builder.placeholder } alt="" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ wrapperClasses }>
|
||||
{ <div dangerouslySetInnerHTML={ { __html: form } } /> }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
jQuery( window )
|
||||
|
||||
// Register custom modules.
|
||||
.on( 'et_builder_api_ready', ( event, API ) => {
|
||||
API.registerModules( [ WPFormsSelector ] );
|
||||
} )
|
||||
|
||||
// Re-initialize WPForms frontend.
|
||||
.on( 'wpformsDiviModuleDisplay', () => {
|
||||
window.wpforms.init();
|
||||
} );
|
||||
|
||||
jQuery( document )
|
||||
.on( 'wpformsReady', function() {
|
||||
const $ = jQuery;
|
||||
|
||||
// Make all the modern dropdowns disabled.
|
||||
$( '.choicesjs-select' ).each( function() {
|
||||
const $instance = $( this ).data( 'choicesjs' );
|
||||
|
||||
if ( $instance && typeof $instance.disable === 'function' ) {
|
||||
$instance.disable();
|
||||
}
|
||||
} );
|
||||
|
||||
// Init Repeater fields.
|
||||
if ( 'undefined' !== typeof WPFormsRepeaterField ) {
|
||||
WPFormsRepeaterField.ready();
|
||||
}
|
||||
} );
|
||||
+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();
|
||||
Executable
+413
File diff suppressed because one or more lines are too long
Vendored
Executable
+1
File diff suppressed because one or more lines are too long
+385
@@ -0,0 +1,385 @@
|
||||
/* global wpforms_gutenberg_form_selector, JSX */
|
||||
/* jshint es3: false, esversion: 6 */
|
||||
|
||||
/**
|
||||
* @param strings.update_wp_notice_head
|
||||
* @param strings.update_wp_notice_text
|
||||
* @param strings.update_wp_notice_link
|
||||
* @param strings.wpforms_empty_help
|
||||
* @param strings.wpforms_empty_info
|
||||
*/
|
||||
|
||||
const { serverSideRender: ServerSideRender = wp.components.ServerSideRender } = wp;
|
||||
const { createElement, Fragment } = wp.element;
|
||||
const { registerBlockType } = wp.blocks;
|
||||
const { InspectorControls } = wp.blockEditor || wp.editor;
|
||||
const { SelectControl, ToggleControl, PanelBody, Placeholder } = wp.components;
|
||||
const { __ } = wp.i18n;
|
||||
|
||||
const wpformsIcon = createElement( 'svg', { width: 20, height: 20, viewBox: '0 0 612 612', className: 'dashicon' },
|
||||
createElement( 'path', {
|
||||
fill: 'currentColor',
|
||||
d: 'M544,0H68C30.445,0,0,30.445,0,68v476c0,37.556,30.445,68,68,68h476c37.556,0,68-30.444,68-68V68 C612,30.445,581.556,0,544,0z M464.44,68L387.6,120.02L323.34,68H464.44z M288.66,68l-64.26,52.02L147.56,68H288.66z M544,544H68 V68h22.1l136,92.14l79.9-64.6l79.56,64.6l136-92.14H544V544z M114.24,263.16h95.88v-48.28h-95.88V263.16z M114.24,360.4h95.88 v-48.62h-95.88V360.4z M242.76,360.4h255v-48.62h-255V360.4L242.76,360.4z M242.76,263.16h255v-48.28h-255V263.16L242.76,263.16z M368.22,457.3h129.54V408H368.22V457.3z',
|
||||
} )
|
||||
);
|
||||
|
||||
/**
|
||||
* Popup container.
|
||||
*
|
||||
* @since 1.8.3
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
let $popup = {};
|
||||
|
||||
/**
|
||||
* Close button (inside the form builder) click event.
|
||||
*
|
||||
* @since 1.8.3
|
||||
*
|
||||
* @param {string} clientID Block Client ID.
|
||||
*/
|
||||
const builderCloseButtonEvent = function( clientID ) {
|
||||
$popup
|
||||
.off( 'wpformsBuilderInPopupClose' )
|
||||
.on( 'wpformsBuilderInPopupClose', function( e, action, formId, formTitle ) {
|
||||
if ( action !== 'saved' || ! formId ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Insert a new block when a new form is created from the popup to update the form list and attributes.
|
||||
const newBlock = wp.blocks.createBlock( 'wpforms/form-selector', {
|
||||
formId: formId.toString(), // Expects string value, make sure we insert string.
|
||||
} );
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
wpforms_gutenberg_form_selector.forms = [ { ID: formId, post_title: formTitle } ];
|
||||
|
||||
// Insert a new block.
|
||||
wp.data.dispatch( 'core/block-editor' ).removeBlock( clientID );
|
||||
wp.data.dispatch( 'core/block-editor' ).insertBlocks( newBlock );
|
||||
} );
|
||||
};
|
||||
|
||||
/**
|
||||
* Init Modern style Dropdown fields (<select>) with choiceJS.
|
||||
*
|
||||
* @since 1.9.0
|
||||
*
|
||||
* @param {Object} e Block Details.
|
||||
*/
|
||||
const loadChoiceJS = function( e ) {
|
||||
if ( typeof window.Choices !== 'function' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const $form = jQuery( e.detail.block.querySelector( `#wpforms-${ e.detail.formId }` ) );
|
||||
const config = window.wpforms_choicesjs_config || {};
|
||||
|
||||
$form.find( '.choicesjs-select' ).each( function( index, element ) {
|
||||
if ( ! ( element instanceof HTMLSelectElement ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const $el = jQuery( element );
|
||||
|
||||
if ( $el.data( 'choicesjs' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const $field = $el.closest( '.wpforms-field' );
|
||||
|
||||
config.callbackOnInit = function() {
|
||||
const self = this,
|
||||
$element = jQuery( self.passedElement.element ),
|
||||
$input = jQuery( self.input.element ),
|
||||
sizeClass = $element.data( 'size-class' );
|
||||
|
||||
// Add CSS-class for size.
|
||||
if ( sizeClass ) {
|
||||
jQuery( 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.removeAttr( 'placeholder' );
|
||||
}
|
||||
}
|
||||
|
||||
this.disable();
|
||||
$field.find( '.is-disabled' ).removeClass( 'is-disabled' );
|
||||
};
|
||||
|
||||
$el.data( 'choicesjs', new window.Choices( element, config ) );
|
||||
|
||||
// Placeholder fix on iframes.
|
||||
if ( $el.val() ) {
|
||||
$el.parent().find( '.choices__input' ).attr( 'style', 'display: none !important' );
|
||||
}
|
||||
} );
|
||||
};
|
||||
|
||||
// on document ready
|
||||
jQuery( function() {
|
||||
jQuery( window ).on( 'wpformsFormSelectorFormLoaded', loadChoiceJS );
|
||||
} );
|
||||
/**
|
||||
* Open builder popup.
|
||||
*
|
||||
* @since 1.6.2
|
||||
*
|
||||
* @param {string} clientID Block Client ID.
|
||||
*/
|
||||
const openBuilderPopup = function( clientID ) {
|
||||
if ( jQuery.isEmptyObject( $popup ) ) {
|
||||
const tmpl = jQuery( '#wpforms-gutenberg-popup' );
|
||||
const parent = jQuery( '#wpwrap' );
|
||||
|
||||
parent.after( tmpl );
|
||||
|
||||
$popup = parent.siblings( '#wpforms-gutenberg-popup' );
|
||||
}
|
||||
|
||||
const url = wpforms_gutenberg_form_selector.get_started_url,
|
||||
$iframe = $popup.find( 'iframe' );
|
||||
|
||||
builderCloseButtonEvent( clientID );
|
||||
$iframe.attr( 'src', url );
|
||||
$popup.fadeIn();
|
||||
};
|
||||
|
||||
const hasForms = function() {
|
||||
return wpforms_gutenberg_form_selector.forms.length > 0;
|
||||
};
|
||||
|
||||
registerBlockType( 'wpforms/form-selector', {
|
||||
title: wpforms_gutenberg_form_selector.strings.title,
|
||||
description: wpforms_gutenberg_form_selector.strings.description,
|
||||
icon: wpformsIcon,
|
||||
keywords: wpforms_gutenberg_form_selector.strings.form_keywords,
|
||||
category: 'widgets',
|
||||
attributes: {
|
||||
formId: {
|
||||
type: 'string',
|
||||
},
|
||||
displayTitle: {
|
||||
type: 'boolean',
|
||||
},
|
||||
displayDesc: {
|
||||
type: 'boolean',
|
||||
},
|
||||
preview: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
example: {
|
||||
attributes: {
|
||||
preview: true,
|
||||
},
|
||||
},
|
||||
supports: {
|
||||
customClassName: hasForms(),
|
||||
},
|
||||
edit( props ) { // eslint-disable-line max-lines-per-function
|
||||
const { attributes: { formId = '', displayTitle = false, displayDesc = false, preview = false }, setAttributes } = props;
|
||||
const formOptions = wpforms_gutenberg_form_selector.forms.map( ( value ) => (
|
||||
{ value: value.ID, label: value.post_title }
|
||||
) );
|
||||
|
||||
const strings = wpforms_gutenberg_form_selector.strings;
|
||||
let jsx;
|
||||
|
||||
formOptions.unshift( { value: '', label: wpforms_gutenberg_form_selector.strings.form_select } );
|
||||
|
||||
function selectForm( value ) { // eslint-disable-line jsdoc/require-jsdoc
|
||||
setAttributes( { formId: value } );
|
||||
}
|
||||
|
||||
function toggleDisplayTitle( value ) { // eslint-disable-line jsdoc/require-jsdoc
|
||||
setAttributes( { displayTitle: value } );
|
||||
}
|
||||
|
||||
function toggleDisplayDesc( value ) { // eslint-disable-line jsdoc/require-jsdoc
|
||||
setAttributes( { displayDesc: value } );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get block empty JSX code.
|
||||
*
|
||||
* @since 1.8.3
|
||||
*
|
||||
* @param {Object} blockProps Block properties.
|
||||
*
|
||||
* @return {JSX.Element} Block empty JSX code.
|
||||
*/
|
||||
function getEmptyFormsPreview( blockProps ) {
|
||||
const clientId = blockProps.clientId;
|
||||
|
||||
return (
|
||||
<Fragment
|
||||
key="wpforms-gutenberg-form-selector-fragment-block-empty">
|
||||
<div className="wpforms-no-form-preview">
|
||||
<img src={ wpforms_gutenberg_form_selector.block_empty_url } alt="" />
|
||||
<p dangerouslySetInnerHTML={ { __html: strings.wpforms_empty_info } }></p>
|
||||
<button type="button" className="get-started-button components-button is-button is-primary"
|
||||
onClick={
|
||||
() => {
|
||||
openBuilderPopup( clientId );
|
||||
}
|
||||
}
|
||||
>
|
||||
{ __( 'Get Started', 'wpforms-lite' ) }
|
||||
</button>
|
||||
<p className="empty-desc" dangerouslySetInnerHTML={ { __html: strings.wpforms_empty_help } }></p>
|
||||
|
||||
{ /* Template for popup with builder iframe */ }
|
||||
<div id="wpforms-gutenberg-popup" className="wpforms-builder-popup">
|
||||
<iframe src="about:blank" width="100%" height="100%" id="wpforms-builder-iframe" title="wpforms-gutenberg-popup"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print empty forms notice.
|
||||
*
|
||||
* @since 1.8.3
|
||||
*
|
||||
* @param {string} clientId Block client ID.
|
||||
*
|
||||
* @return {JSX.Element} Field styles JSX code.
|
||||
*/
|
||||
function printEmptyFormsNotice( clientId ) {
|
||||
return (
|
||||
<InspectorControls key="wpforms-gutenberg-form-selector-inspector-main-settings">
|
||||
<PanelBody className="wpforms-gutenberg-panel" title={ strings.form_settings }>
|
||||
<p className="wpforms-gutenberg-panel-notice wpforms-warning wpforms-empty-form-notice" style={ { display: 'block' } }>
|
||||
<strong>{ __( 'You haven’t created a form, yet!', 'wpforms-lite' ) }</strong>
|
||||
{ __( 'What are you waiting for?', 'wpforms-lite' ) }
|
||||
</p>
|
||||
<button type="button" className="get-started-button components-button is-button is-secondary"
|
||||
onClick={
|
||||
() => {
|
||||
openBuilderPopup( clientId );
|
||||
}
|
||||
}
|
||||
>
|
||||
{ __( 'Get Started', 'wpforms-lite' ) }
|
||||
</button>
|
||||
</PanelBody>
|
||||
</InspectorControls>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get styling panels preview.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @return {JSX.Element} JSX code.
|
||||
*/
|
||||
function getStylingPanelsPreview() {
|
||||
return (
|
||||
<Fragment>
|
||||
<PanelBody className="wpforms-gutenberg-panel disabled_panel" title={ strings.themes }>
|
||||
<div className="wpforms-panel-preview wpforms-panel-preview-themes"></div>
|
||||
</PanelBody>
|
||||
<PanelBody className="wpforms-gutenberg-panel disabled_panel" title={ strings.field_styles }>
|
||||
<div className="wpforms-panel-preview wpforms-panel-preview-field"></div>
|
||||
</PanelBody>
|
||||
<PanelBody className="wpforms-gutenberg-panel disabled_panel" title={ strings.label_styles }>
|
||||
<div className="wpforms-panel-preview wpforms-panel-preview-label"></div>
|
||||
</PanelBody>
|
||||
<PanelBody className="wpforms-gutenberg-panel disabled_panel" title={ strings.button_styles }>
|
||||
<div className="wpforms-panel-preview wpforms-panel-preview-button"></div>
|
||||
</PanelBody>
|
||||
<PanelBody className="wpforms-gutenberg-panel disabled_panel" title={ strings.container_styles }>
|
||||
<div className="wpforms-panel-preview wpforms-panel-preview-container"></div>
|
||||
</PanelBody>
|
||||
<PanelBody className="wpforms-gutenberg-panel disabled_panel" title={ strings.background_styles }>
|
||||
<div className="wpforms-panel-preview wpforms-panel-preview-background"></div>
|
||||
</PanelBody>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! hasForms() ) {
|
||||
jsx = [ printEmptyFormsNotice( props.clientId ) ];
|
||||
|
||||
jsx.push( getEmptyFormsPreview( props ) );
|
||||
return jsx;
|
||||
}
|
||||
|
||||
jsx = [
|
||||
<InspectorControls key="wpforms-gutenberg-form-selector-inspector-controls">
|
||||
<PanelBody title={ wpforms_gutenberg_form_selector.strings.form_settings }>
|
||||
<SelectControl
|
||||
label={ wpforms_gutenberg_form_selector.strings.form_selected }
|
||||
value={ formId }
|
||||
options={ formOptions }
|
||||
onChange={ selectForm }
|
||||
/>
|
||||
<ToggleControl
|
||||
label={ wpforms_gutenberg_form_selector.strings.show_title }
|
||||
checked={ displayTitle }
|
||||
onChange={ toggleDisplayTitle }
|
||||
/>
|
||||
<ToggleControl
|
||||
label={ wpforms_gutenberg_form_selector.strings.show_description }
|
||||
checked={ displayDesc }
|
||||
onChange={ toggleDisplayDesc }
|
||||
/>
|
||||
<p className="wpforms-gutenberg-panel-notice wpforms-warning">
|
||||
<strong>{ strings.update_wp_notice_head }</strong>
|
||||
{ strings.update_wp_notice_text } <a href={ strings.update_wp_notice_link } rel="noreferrer" target="_blank">{ strings.learn_more }</a>
|
||||
</p>
|
||||
</PanelBody>
|
||||
{ getStylingPanelsPreview() }
|
||||
</InspectorControls>,
|
||||
];
|
||||
|
||||
if ( formId ) {
|
||||
jsx.push(
|
||||
<ServerSideRender
|
||||
key="wpforms-gutenberg-form-selector-server-side-renderer"
|
||||
block="wpforms/form-selector"
|
||||
attributes={ props.attributes }
|
||||
/>
|
||||
);
|
||||
} else if ( preview ) {
|
||||
jsx.push(
|
||||
<Fragment
|
||||
key="wpforms-gutenberg-form-selector-fragment-block-preview">
|
||||
<img src={ wpforms_gutenberg_form_selector.block_preview_url } style={ { width: '100%' } } alt="" />
|
||||
</Fragment>
|
||||
);
|
||||
} else {
|
||||
jsx.push(
|
||||
<Placeholder
|
||||
key="wpforms-gutenberg-form-selector-wrap"
|
||||
className="wpforms-gutenberg-form-selector-wrap">
|
||||
<img src={ wpforms_gutenberg_form_selector.logo_url } alt="" />
|
||||
<SelectControl
|
||||
key="wpforms-gutenberg-form-selector-select-control"
|
||||
value={ formId }
|
||||
options={ formOptions }
|
||||
onChange={ selectForm }
|
||||
/>
|
||||
</Placeholder>
|
||||
);
|
||||
}
|
||||
|
||||
return jsx;
|
||||
},
|
||||
save() {
|
||||
return null;
|
||||
},
|
||||
} );
|
||||
Executable
+164
@@ -0,0 +1,164 @@
|
||||
/* global wpforms_gutenberg_form_selector */
|
||||
/* jshint es3: false, esversion: 6 */
|
||||
|
||||
/**
|
||||
* @param strings.custom_css
|
||||
* @param strings.custom_css_notice
|
||||
* @param strings.copy_paste_settings
|
||||
* @param strings.copy_paste_notice
|
||||
*/
|
||||
|
||||
/**
|
||||
* Gutenberg editor block.
|
||||
*
|
||||
* Advanced Settings module.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*/
|
||||
export default ( function( $ ) {
|
||||
/**
|
||||
* WP core components.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*/
|
||||
const { addFilter } = wp.hooks;
|
||||
const { createHigherOrderComponent } = wp.compose;
|
||||
const { Fragment } = wp.element;
|
||||
const { InspectorAdvancedControls } = wp.blockEditor || wp.editor;
|
||||
const { TextareaControl } = wp.components;
|
||||
|
||||
/**
|
||||
* Localized data aliases.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*/
|
||||
const { strings } = wpforms_gutenberg_form_selector;
|
||||
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const app = {
|
||||
/**
|
||||
* Initialize module.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @param {Object} commonModule Common module.
|
||||
*/
|
||||
init( commonModule ) {
|
||||
app.common = commonModule;
|
||||
|
||||
app.hooks();
|
||||
app.events();
|
||||
},
|
||||
|
||||
/**
|
||||
* Hooks.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*/
|
||||
hooks() {
|
||||
addFilter(
|
||||
'editor.BlockEdit',
|
||||
'editorskit/custom-advanced-control',
|
||||
app.withAdvancedControls
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Events.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*/
|
||||
events() {
|
||||
$( document )
|
||||
.on( 'focus click', 'textarea', app.copyPasteFocus );
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy / Paste Style Settings textarea focus event.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*/
|
||||
copyPasteFocus() {
|
||||
const $input = $( this );
|
||||
|
||||
if ( $input.siblings( 'label' ).text() === strings.copy_paste_settings ) {
|
||||
// Select all text, so it's easier to copy and paste value.
|
||||
$input.select();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get fields.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @param {Object} props Block properties.
|
||||
*
|
||||
* @return {Object} Inspector advanced controls JSX code.
|
||||
*/
|
||||
getFields( props ) {
|
||||
// Proceed only for WPForms block and when form ID is set.
|
||||
if ( props?.name !== 'wpforms/form-selector' || ! props?.attributes?.formId ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Common event handlers.
|
||||
const handlers = app.common.getSettingsFieldsHandlers( props );
|
||||
|
||||
return (
|
||||
<InspectorAdvancedControls>
|
||||
<div className={ app.common.getPanelClass( props ) + ' advanced' }>
|
||||
<TextareaControl
|
||||
className="wpforms-gutenberg-form-selector-custom-css"
|
||||
label={ strings.custom_css }
|
||||
rows="5"
|
||||
spellCheck="false"
|
||||
value={ props.attributes.customCss }
|
||||
onChange={ ( value ) => handlers.attrChange( 'customCss', value ) }
|
||||
/>
|
||||
<div className="wpforms-gutenberg-form-selector-legend" dangerouslySetInnerHTML={ { __html: strings.custom_css_notice } }></div>
|
||||
<TextareaControl
|
||||
className="wpforms-gutenberg-form-selector-copy-paste-settings"
|
||||
label={ strings.copy_paste_settings }
|
||||
rows="4"
|
||||
spellCheck="false"
|
||||
value={ props.attributes.copyPasteJsonValue }
|
||||
onChange={ ( value ) => handlers.pasteSettings( value ) }
|
||||
/>
|
||||
<div className="wpforms-gutenberg-form-selector-legend" dangerouslySetInnerHTML={ { __html: strings.copy_paste_notice } }></div>
|
||||
</div>
|
||||
</InspectorAdvancedControls>
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Add controls on Advanced Settings Panel.
|
||||
*
|
||||
* @param {Function} BlockEdit Block edit component.
|
||||
*
|
||||
* @return {Function} BlockEdit Modified block edit component.
|
||||
*/
|
||||
withAdvancedControls: createHigherOrderComponent(
|
||||
( BlockEdit ) => {
|
||||
return ( props ) => {
|
||||
return (
|
||||
<Fragment>
|
||||
<BlockEdit { ...props } />
|
||||
{ app.getFields( props ) }
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
},
|
||||
'withAdvancedControls'
|
||||
),
|
||||
};
|
||||
|
||||
// Provide access to public functions/properties.
|
||||
return app;
|
||||
}( jQuery ) );
|
||||
Executable
+66
@@ -0,0 +1,66 @@
|
||||
/* global wpforms_gutenberg_form_selector */
|
||||
/* jshint es3: false, esversion: 6 */
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
/**
|
||||
* @param strings.remove_image
|
||||
*/
|
||||
|
||||
/**
|
||||
* React component for the background preview.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @param {Object} props Component props.
|
||||
* @param {Object} props.attributes Block attributes.
|
||||
* @param {Function} props.onRemoveBackground Function to remove the background.
|
||||
* @param {Function} props.onPreviewClicked Function to handle the preview click.
|
||||
*
|
||||
* @return {Object} React component.
|
||||
*/
|
||||
const BackgroundPreview = ( { attributes, onRemoveBackground, onPreviewClicked } ) => {
|
||||
const { Button } = wp.components;
|
||||
const { strings } = wpforms_gutenberg_form_selector;
|
||||
|
||||
return (
|
||||
<div className="wpforms-gutenberg-form-selector-background-preview">
|
||||
<style>
|
||||
{ `
|
||||
.wpforms-gutenberg-form-selector-background-preview-image {
|
||||
--wpforms-background-url: ${ attributes.backgroundUrl };
|
||||
}
|
||||
` }
|
||||
</style>
|
||||
<input
|
||||
className="wpforms-gutenberg-form-selector-background-preview-image"
|
||||
onClick={ onPreviewClicked }
|
||||
tabIndex={ 0 }
|
||||
type="button"
|
||||
onKeyDown={
|
||||
( event ) => {
|
||||
if ( event.key === 'Enter' || event.key === ' ' ) {
|
||||
onPreviewClicked();
|
||||
}
|
||||
}
|
||||
}
|
||||
>
|
||||
</input>
|
||||
<Button
|
||||
isSecondary
|
||||
className="is-destructive"
|
||||
onClick={ onRemoveBackground }
|
||||
>
|
||||
{ strings.remove_image }
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
BackgroundPreview.propTypes = {
|
||||
attributes: PropTypes.object.isRequired,
|
||||
onRemoveBackground: PropTypes.func.isRequired,
|
||||
onPreviewClicked: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default BackgroundPreview;
|
||||
Executable
+607
@@ -0,0 +1,607 @@
|
||||
/* global wpforms_gutenberg_form_selector */
|
||||
/* jshint es3: false, esversion: 6 */
|
||||
|
||||
import BackgroundPreview from './background-preview.js';
|
||||
|
||||
/**
|
||||
* @param strings.background_styles
|
||||
* @param strings.bottom_center
|
||||
* @param strings.bottom_left
|
||||
* @param strings.bottom_right
|
||||
* @param strings.center_center
|
||||
* @param strings.center_left
|
||||
* @param strings.center_right
|
||||
* @param strings.choose_image
|
||||
* @param strings.image_url
|
||||
* @param strings.media_library
|
||||
* @param strings.no_repeat
|
||||
* @param strings.repeat_x
|
||||
* @param strings.repeat_y
|
||||
* @param strings.select_background_image
|
||||
* @param strings.select_image
|
||||
* @param strings.stock_photo
|
||||
* @param strings.tile
|
||||
* @param strings.top_center
|
||||
* @param strings.top_left
|
||||
* @param strings.top_right
|
||||
*/
|
||||
|
||||
/**
|
||||
* Gutenberg editor block.
|
||||
*
|
||||
* Background styles panel module.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*/
|
||||
export default ( function() {
|
||||
/**
|
||||
* WP core components.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*/
|
||||
const { PanelColorSettings } = wp.blockEditor || wp.editor;
|
||||
const { SelectControl, PanelBody, Flex, FlexBlock, __experimentalUnitControl, TextControl, Button } = wp.components;
|
||||
|
||||
/**
|
||||
* Localized data aliases.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*/
|
||||
const { strings, defaults } = wpforms_gutenberg_form_selector;
|
||||
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const app = {
|
||||
|
||||
/**
|
||||
* Get block attributes.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @return {Object} Block attributes.
|
||||
*/
|
||||
getBlockAttributes() {
|
||||
return {
|
||||
backgroundImage: {
|
||||
type: 'string',
|
||||
default: defaults.backgroundImage,
|
||||
},
|
||||
backgroundPosition: {
|
||||
type: 'string',
|
||||
default: defaults.backgroundPosition,
|
||||
},
|
||||
backgroundRepeat: {
|
||||
type: 'string',
|
||||
default: defaults.backgroundRepeat,
|
||||
},
|
||||
backgroundSizeMode: {
|
||||
type: 'string',
|
||||
default: defaults.backgroundSizeMode,
|
||||
},
|
||||
backgroundSize: {
|
||||
type: 'string',
|
||||
default: defaults.backgroundSize,
|
||||
},
|
||||
backgroundWidth: {
|
||||
type: 'string',
|
||||
default: defaults.backgroundWidth,
|
||||
},
|
||||
backgroundHeight: {
|
||||
type: 'string',
|
||||
default: defaults.backgroundHeight,
|
||||
},
|
||||
backgroundColor: {
|
||||
type: 'string',
|
||||
default: defaults.backgroundColor,
|
||||
},
|
||||
backgroundUrl: {
|
||||
type: 'string',
|
||||
default: defaults.backgroundUrl,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Get Background Styles panel JSX code.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @param {Object} props Block properties.
|
||||
* @param {Object} handlers Block handlers.
|
||||
* @param {Object} formSelectorCommon Block properties.
|
||||
* @param {Object} stockPhotos Stock Photos module.
|
||||
* @param {Object} uiState UI state.
|
||||
*
|
||||
* @return {Object} Field styles JSX code.
|
||||
*/
|
||||
getBackgroundStyles( props, handlers, formSelectorCommon, stockPhotos, uiState ) { // eslint-disable-line max-lines-per-function, complexity
|
||||
const isNotDisabled = uiState.isNotDisabled;
|
||||
const isProEnabled = uiState.isProEnabled;
|
||||
const showBackgroundPreview = uiState.showBackgroundPreview;
|
||||
const setShowBackgroundPreview = uiState.setShowBackgroundPreview;
|
||||
const lastBgImage = uiState.lastBgImage;
|
||||
const setLastBgImage = uiState.setLastBgImage;
|
||||
const tabIndex = isNotDisabled ? 0 : -1;
|
||||
const cssClass = formSelectorCommon.getPanelClass( props ) + ( isNotDisabled ? '' : ' wpforms-gutenberg-panel-disabled' );
|
||||
|
||||
return (
|
||||
<PanelBody className={ cssClass } title={ strings.background_styles }>
|
||||
<div // eslint-disable-line jsx-a11y/no-static-element-interactions
|
||||
className="wpforms-gutenberg-form-selector-panel-body"
|
||||
onClick={ ( event ) => {
|
||||
if ( isNotDisabled ) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.stopPropagation();
|
||||
|
||||
if ( ! isProEnabled ) {
|
||||
return formSelectorCommon.education.showProModal( 'background', strings.background_styles );
|
||||
}
|
||||
|
||||
formSelectorCommon.education.showLicenseModal( 'background', strings.background_styles, 'background-styles' );
|
||||
} }
|
||||
onKeyDown={ ( event ) => {
|
||||
if ( isNotDisabled ) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.stopPropagation();
|
||||
|
||||
if ( ! isProEnabled ) {
|
||||
return formSelectorCommon.education.showProModal( 'background', strings.background_styles );
|
||||
}
|
||||
|
||||
formSelectorCommon.education.showLicenseModal( 'background', strings.background_styles, 'background-styles' );
|
||||
} }
|
||||
>
|
||||
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
|
||||
<FlexBlock>
|
||||
<SelectControl
|
||||
label={ strings.image }
|
||||
tabIndex={ tabIndex }
|
||||
value={ props.attributes.backgroundImage }
|
||||
options={ [
|
||||
{ label: strings.none, value: 'none' },
|
||||
{ label: strings.media_library, value: 'library' },
|
||||
{ label: strings.stock_photo, value: 'stock' },
|
||||
] }
|
||||
onChange={ ( value ) => app.setContainerBackgroundImageWrapper( props, handlers, value, lastBgImage, setLastBgImage ) }
|
||||
/>
|
||||
</FlexBlock>
|
||||
<FlexBlock>
|
||||
{ ( props.attributes.backgroundImage !== 'none' || ! isNotDisabled ) && (
|
||||
<SelectControl
|
||||
label={ strings.position }
|
||||
value={ props.attributes.backgroundPosition }
|
||||
tabIndex={ tabIndex }
|
||||
options={ [
|
||||
{ label: strings.top_left, value: 'top left' },
|
||||
{ label: strings.top_center, value: 'top center' },
|
||||
{ label: strings.top_right, value: 'top right' },
|
||||
{ label: strings.center_left, value: 'center left' },
|
||||
{ label: strings.center_center, value: 'center center' },
|
||||
{ label: strings.center_right, value: 'center right' },
|
||||
{ label: strings.bottom_left, value: 'bottom left' },
|
||||
{ label: strings.bottom_center, value: 'bottom center' },
|
||||
{ label: strings.bottom_right, value: 'bottom right' },
|
||||
] }
|
||||
disabled={ ( props.attributes.backgroundImage === 'none' && isNotDisabled ) }
|
||||
onChange={ ( value ) => handlers.styleAttrChange( 'backgroundPosition', value ) }
|
||||
/>
|
||||
) }
|
||||
</FlexBlock>
|
||||
</Flex>
|
||||
{ ( props.attributes.backgroundImage !== 'none' || ! isNotDisabled ) && (
|
||||
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
|
||||
<FlexBlock>
|
||||
<SelectControl
|
||||
label={ strings.repeat }
|
||||
tabIndex={ tabIndex }
|
||||
value={ props.attributes.backgroundRepeat }
|
||||
options={ [
|
||||
{ label: strings.no_repeat, value: 'no-repeat' },
|
||||
{ label: strings.tile, value: 'repeat' },
|
||||
{ label: strings.repeat_x, value: 'repeat-x' },
|
||||
{ label: strings.repeat_y, value: 'repeat-y' },
|
||||
] }
|
||||
disabled={ ( props.attributes.backgroundImage === 'none' && isNotDisabled ) }
|
||||
onChange={ ( value ) => handlers.styleAttrChange( 'backgroundRepeat', value ) }
|
||||
/>
|
||||
</FlexBlock>
|
||||
<FlexBlock>
|
||||
<SelectControl
|
||||
label={ strings.size }
|
||||
tabIndex={ tabIndex }
|
||||
value={ props.attributes.backgroundSizeMode }
|
||||
options={ [
|
||||
{ label: strings.dimensions, value: 'dimensions' },
|
||||
{ label: strings.cover, value: 'cover' },
|
||||
] }
|
||||
disabled={ ( props.attributes.backgroundImage === 'none' && isNotDisabled ) }
|
||||
onChange={ ( value ) => app.handleSizeFromDimensions( props, handlers, value ) }
|
||||
/>
|
||||
</FlexBlock>
|
||||
</Flex>
|
||||
) }
|
||||
{ ( ( props.attributes.backgroundSizeMode === 'dimensions' && props.attributes.backgroundImage !== 'none' ) || ! isNotDisabled ) && (
|
||||
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
|
||||
<FlexBlock>
|
||||
<__experimentalUnitControl
|
||||
label={ strings.width }
|
||||
tabIndex={ tabIndex }
|
||||
value={ props.attributes.backgroundWidth }
|
||||
isUnitSelectTabbable={ isNotDisabled }
|
||||
onChange={ ( value ) => app.handleSizeFromWidth( props, handlers, value ) }
|
||||
/>
|
||||
</FlexBlock>
|
||||
<FlexBlock>
|
||||
<__experimentalUnitControl
|
||||
label={ strings.height }
|
||||
tabIndex={ tabIndex }
|
||||
value={ props.attributes.backgroundHeight }
|
||||
isUnitSelectTabbable={ isNotDisabled }
|
||||
onChange={ ( value ) => app.handleSizeFromHeight( props, handlers, value ) }
|
||||
/>
|
||||
</FlexBlock>
|
||||
</Flex>
|
||||
) }
|
||||
{ ( ! showBackgroundPreview || props.attributes.backgroundUrl === 'url()' ) && (
|
||||
( props.attributes.backgroundImage === 'library' && (
|
||||
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
|
||||
<FlexBlock>
|
||||
<Button
|
||||
isSecondary
|
||||
tabIndex={ tabIndex }
|
||||
className={ 'wpforms-gutenberg-form-selector-media-library-button' }
|
||||
onClick={ app.openMediaLibrary.bind( null, props, handlers, setShowBackgroundPreview ) }
|
||||
>
|
||||
{ strings.choose_image }
|
||||
</Button>
|
||||
</FlexBlock>
|
||||
</Flex>
|
||||
) ) || ( props.attributes.backgroundImage === 'stock' && (
|
||||
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
|
||||
<FlexBlock>
|
||||
<Button
|
||||
isSecondary
|
||||
tabIndex={ tabIndex }
|
||||
className={ 'wpforms-gutenberg-form-selector-media-library-button' }
|
||||
onClick={ stockPhotos?.openModal.bind( null, props, handlers, 'bg-styles', setShowBackgroundPreview ) }
|
||||
>
|
||||
{ strings.choose_image }
|
||||
</Button>
|
||||
</FlexBlock>
|
||||
</Flex>
|
||||
) )
|
||||
) }
|
||||
{ ( ( showBackgroundPreview && props.attributes.backgroundImage !== 'none' ) || props.attributes.backgroundUrl !== 'url()' ) && (
|
||||
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
|
||||
<FlexBlock>
|
||||
<div>
|
||||
<BackgroundPreview
|
||||
attributes={ props.attributes }
|
||||
onRemoveBackground={
|
||||
() => {
|
||||
app.onRemoveBackground( setShowBackgroundPreview, handlers, setLastBgImage );
|
||||
}
|
||||
}
|
||||
onPreviewClicked={ () => {
|
||||
if ( props.attributes.backgroundImage === 'library' ) {
|
||||
return app.openMediaLibrary( props, handlers, setShowBackgroundPreview );
|
||||
}
|
||||
|
||||
return stockPhotos?.openModal( props, handlers, 'bg-styles', setShowBackgroundPreview );
|
||||
} }
|
||||
/>
|
||||
</div>
|
||||
<TextControl
|
||||
label={ strings.image_url }
|
||||
tabIndex={ tabIndex }
|
||||
value={ props.attributes.backgroundImage !== 'none' && props.attributes.backgroundUrl }
|
||||
className={ 'wpforms-gutenberg-form-selector-image-url' }
|
||||
onChange={ ( value ) => handlers.styleAttrChange( 'backgroundUrl', value ) }
|
||||
onLoad={ ( value ) => props.attributes.backgroundImage !== 'none' && handlers.styleAttrChange( 'backgroundUrl', value ) }
|
||||
/>
|
||||
</FlexBlock>
|
||||
</Flex>
|
||||
) }
|
||||
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
|
||||
<FlexBlock>
|
||||
<div className="wpforms-gutenberg-form-selector-control-label">{ strings.colors }</div>
|
||||
<PanelColorSettings
|
||||
__experimentalIsRenderedInSidebar
|
||||
enableAlpha
|
||||
showTitle={ false }
|
||||
tabIndex={ tabIndex }
|
||||
className="wpforms-gutenberg-form-selector-color-panel"
|
||||
colorSettings={ [
|
||||
{
|
||||
value: props.attributes.backgroundColor,
|
||||
onChange: ( value ) => {
|
||||
if ( ! isNotDisabled ) {
|
||||
return;
|
||||
}
|
||||
|
||||
handlers.styleAttrChange( 'backgroundColor', value );
|
||||
},
|
||||
label: strings.background,
|
||||
},
|
||||
] }
|
||||
/>
|
||||
</FlexBlock>
|
||||
</Flex>
|
||||
</div>
|
||||
</PanelBody>
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Open media library modal and handle image selection.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @param {Object} props Block properties.
|
||||
* @param {Object} handlers Block handlers.
|
||||
* @param {Function} setShowBackgroundPreview Set show background preview.
|
||||
*/
|
||||
openMediaLibrary( props, handlers, setShowBackgroundPreview ) {
|
||||
const frame = wp.media( {
|
||||
title: strings.select_background_image,
|
||||
multiple: false,
|
||||
library: {
|
||||
type: 'image',
|
||||
},
|
||||
button: {
|
||||
text: strings.select_image,
|
||||
},
|
||||
} );
|
||||
|
||||
frame.on( 'select', () => {
|
||||
const attachment = frame.state().get( 'selection' ).first().toJSON();
|
||||
const setAttr = {};
|
||||
const attribute = 'backgroundUrl';
|
||||
|
||||
if ( attachment.url ) {
|
||||
const value = `url(${ attachment.url })`;
|
||||
|
||||
setAttr[ attribute ] = value;
|
||||
|
||||
props.setAttributes( setAttr );
|
||||
|
||||
handlers.styleAttrChange( 'backgroundUrl', value );
|
||||
|
||||
setShowBackgroundPreview( true );
|
||||
}
|
||||
} );
|
||||
|
||||
frame.open();
|
||||
},
|
||||
|
||||
/**
|
||||
* Set container background image.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @param {HTMLElement} container Container element.
|
||||
* @param {string} value Value.
|
||||
*
|
||||
* @return {boolean} True if the value was set, false otherwise.
|
||||
*/
|
||||
setContainerBackgroundImage( container, value ) {
|
||||
if ( value === 'none' ) {
|
||||
container.style.setProperty( `--wpforms-background-url`, 'url()' );
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set container background image.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @param {Object} props Block properties.
|
||||
* @param {Object} handlers Block event handlers.
|
||||
* @param {string} value Value.
|
||||
* @param {string} lastBgImage Last background image.
|
||||
* @param {Function} setLastBgImage Set last background image.
|
||||
*/
|
||||
setContainerBackgroundImageWrapper( props, handlers, value, lastBgImage, setLastBgImage ) {
|
||||
if ( value === 'none' ) {
|
||||
setLastBgImage( props.attributes.backgroundUrl );
|
||||
props.attributes.backgroundUrl = 'url()';
|
||||
|
||||
handlers.styleAttrChange( 'backgroundUrl', 'url()' );
|
||||
} else if ( lastBgImage ) {
|
||||
props.attributes.backgroundUrl = lastBgImage;
|
||||
handlers.styleAttrChange( 'backgroundUrl', lastBgImage );
|
||||
}
|
||||
|
||||
handlers.styleAttrChange( 'backgroundImage', value );
|
||||
},
|
||||
|
||||
/**
|
||||
* Set container background position.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @param {HTMLElement} container Container element.
|
||||
* @param {string} value Value.
|
||||
*
|
||||
* @return {boolean} True if the value was set, false otherwise.
|
||||
*/
|
||||
setContainerBackgroundPosition( container, value ) {
|
||||
container.style.setProperty( `--wpforms-background-position`, value );
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set container background repeat.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @param {HTMLElement} container Container element.
|
||||
* @param {string} value Value.
|
||||
*
|
||||
* @return {boolean} True if the value was set, false otherwise.
|
||||
*/
|
||||
setContainerBackgroundRepeat( container, value ) {
|
||||
container.style.setProperty( `--wpforms-background-repeat`, value );
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle real size from dimensions.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @param {Object} props Block properties.
|
||||
* @param {Object} handlers Block handlers.
|
||||
* @param {string} value Value.
|
||||
*/
|
||||
handleSizeFromDimensions( props, handlers, value ) {
|
||||
if ( value === 'cover' ) {
|
||||
props.attributes.backgroundSize = 'cover';
|
||||
|
||||
handlers.styleAttrChange( 'backgroundWidth', props.attributes.backgroundWidth );
|
||||
handlers.styleAttrChange( 'backgroundHeight', props.attributes.backgroundHeight );
|
||||
handlers.styleAttrChange( 'backgroundSizeMode', 'cover' );
|
||||
handlers.styleAttrChange( 'backgroundSize', 'cover' );
|
||||
} else {
|
||||
props.attributes.backgroundSize = 'dimensions';
|
||||
|
||||
handlers.styleAttrChange( 'backgroundSizeMode', 'dimensions' );
|
||||
handlers.styleAttrChange( 'backgroundSize', props.attributes.backgroundWidth + ' ' + props.attributes.backgroundHeight );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle real size from width.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @param {Object} props Block properties.
|
||||
* @param {Object} handlers Block handlers.
|
||||
* @param {string} value Value.
|
||||
*/
|
||||
handleSizeFromWidth( props, handlers, value ) {
|
||||
props.attributes.backgroundSize = value + ' ' + props.attributes.backgroundHeight;
|
||||
props.attributes.backgroundWidth = value;
|
||||
|
||||
handlers.styleAttrChange( 'backgroundSize', value + ' ' + props.attributes.backgroundHeight );
|
||||
handlers.styleAttrChange( 'backgroundWidth', value );
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle real size from height.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @param {Object} props Block properties.
|
||||
* @param {Object} handlers Block handlers.
|
||||
* @param {string} value Value.
|
||||
*/
|
||||
handleSizeFromHeight( props, handlers, value ) {
|
||||
props.attributes.backgroundSize = props.attributes.backgroundWidth + ' ' + value;
|
||||
props.attributes.backgroundHeight = value;
|
||||
|
||||
handlers.styleAttrChange( 'backgroundSize', props.attributes.backgroundWidth + ' ' + value );
|
||||
handlers.styleAttrChange( 'backgroundHeight', value );
|
||||
},
|
||||
|
||||
/**
|
||||
* Set container background width.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @param {HTMLElement} container Container element.
|
||||
* @param {string} value Value.
|
||||
*
|
||||
* @return {boolean} True if the value was set, false otherwise.
|
||||
*/
|
||||
setContainerBackgroundWidth( container, value ) {
|
||||
container.style.setProperty( `--wpforms-background-width`, value );
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set container background height.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @param {HTMLElement} container Container element.
|
||||
* @param {string} value Value.
|
||||
*
|
||||
* @return {boolean} True if the value was set, false otherwise.
|
||||
*/
|
||||
setContainerBackgroundHeight( container, value ) {
|
||||
container.style.setProperty( `--wpforms-background-height`, value );
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set container background url.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @param {HTMLElement} container Container element.
|
||||
* @param {string} value Value.
|
||||
*
|
||||
* @return {boolean} True if the value was set, false otherwise.
|
||||
*/
|
||||
setBackgroundUrl( container, value ) {
|
||||
container.style.setProperty( `--wpforms-background-url`, value );
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set container background color.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @param {HTMLElement} container Container element.
|
||||
* @param {string} value Value.
|
||||
*
|
||||
* @return {boolean} True if the value was set, false otherwise.
|
||||
*/
|
||||
setBackgroundColor( container, value ) {
|
||||
container.style.setProperty( `--wpforms-background-color`, value );
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_showBackgroundPreview( props ) {
|
||||
return props.attributes.backgroundImage !== 'none' &&
|
||||
props.attributes.backgroundUrl &&
|
||||
props.attributes.backgroundUrl !== 'url()';
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove background image.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @param {Function} setShowBackgroundPreview Set show background preview.
|
||||
* @param {Object} handlers Block handlers.
|
||||
* @param {Function} setLastBgImage Set last background image.
|
||||
*/
|
||||
onRemoveBackground( setShowBackgroundPreview, handlers, setLastBgImage ) {
|
||||
setShowBackgroundPreview( false );
|
||||
handlers.styleAttrChange( 'backgroundUrl', 'url()' );
|
||||
setLastBgImage( '' );
|
||||
},
|
||||
};
|
||||
|
||||
return app;
|
||||
}() );
|
||||
Executable
+181
@@ -0,0 +1,181 @@
|
||||
/* global wpforms_gutenberg_form_selector */
|
||||
/* jshint es3: false, esversion: 6 */
|
||||
|
||||
/**
|
||||
* @param strings.border_radius
|
||||
* @param strings.border_size
|
||||
* @param strings.button_color_notice
|
||||
* @param strings.button_styles
|
||||
* @param strings.dashed
|
||||
* @param strings.solid
|
||||
*/
|
||||
|
||||
/**
|
||||
* Gutenberg editor block.
|
||||
*
|
||||
* Button styles panel module.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*/
|
||||
export default ( ( function() {
|
||||
/**
|
||||
* WP core components.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*/
|
||||
const { PanelColorSettings } = wp.blockEditor || wp.editor;
|
||||
const { SelectControl, PanelBody, Flex, FlexBlock, __experimentalUnitControl } = wp.components;
|
||||
|
||||
/**
|
||||
* Localized data aliases.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*/
|
||||
const { strings, defaults } = wpforms_gutenberg_form_selector;
|
||||
|
||||
// noinspection UnnecessaryLocalVariableJS
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const app = {
|
||||
|
||||
/**
|
||||
* Get block attributes.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @return {Object} Block attributes.
|
||||
*/
|
||||
getBlockAttributes() {
|
||||
return {
|
||||
buttonSize: {
|
||||
type: 'string',
|
||||
default: defaults.buttonSize,
|
||||
},
|
||||
buttonBorderStyle: {
|
||||
type: 'string',
|
||||
default: defaults.buttonBorderStyle,
|
||||
},
|
||||
buttonBorderSize: {
|
||||
type: 'string',
|
||||
default: defaults.buttonBorderSize,
|
||||
},
|
||||
buttonBorderRadius: {
|
||||
type: 'string',
|
||||
default: defaults.buttonBorderRadius,
|
||||
},
|
||||
buttonBackgroundColor: {
|
||||
type: 'string',
|
||||
default: defaults.buttonBackgroundColor,
|
||||
},
|
||||
buttonTextColor: {
|
||||
type: 'string',
|
||||
default: defaults.buttonTextColor,
|
||||
},
|
||||
buttonBorderColor: {
|
||||
type: 'string',
|
||||
default: defaults.buttonBorderColor,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Get Button styles JSX code.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @param {Object} props Block properties.
|
||||
* @param {Object} handlers Block event handlers.
|
||||
* @param {Object} sizeOptions Size selector options.
|
||||
* @param {Object} formSelectorCommon Form selector common object.
|
||||
*
|
||||
* @return {Object} Button styles JSX code.
|
||||
*/
|
||||
getButtonStyles( props, handlers, sizeOptions, formSelectorCommon ) { // eslint-disable-line max-lines-per-function
|
||||
return (
|
||||
<PanelBody className={ formSelectorCommon.getPanelClass( props ) } title={ strings.button_styles }>
|
||||
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
|
||||
<FlexBlock>
|
||||
<SelectControl
|
||||
label={ strings.size }
|
||||
value={ props.attributes.buttonSize }
|
||||
options={ sizeOptions }
|
||||
onChange={ ( value ) => handlers.styleAttrChange( 'buttonSize', value ) }
|
||||
/>
|
||||
</FlexBlock>
|
||||
<FlexBlock>
|
||||
<SelectControl
|
||||
label={ strings.border }
|
||||
value={ props.attributes.buttonBorderStyle }
|
||||
options={
|
||||
[
|
||||
{ label: strings.none, value: 'none' },
|
||||
{ label: strings.solid, value: 'solid' },
|
||||
{ label: strings.dashed, value: 'dashed' },
|
||||
{ label: strings.dotted, value: 'dotted' },
|
||||
]
|
||||
}
|
||||
onChange={ ( value ) => handlers.styleAttrChange( 'buttonBorderStyle', value ) }
|
||||
/>
|
||||
</FlexBlock>
|
||||
</Flex>
|
||||
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
|
||||
<FlexBlock>
|
||||
<__experimentalUnitControl
|
||||
label={ strings.border_size }
|
||||
value={ props.attributes.buttonBorderStyle === 'none' ? '' : props.attributes.buttonBorderSize }
|
||||
min={ 0 }
|
||||
disabled={ props.attributes.buttonBorderStyle === 'none' }
|
||||
onChange={ ( value ) => handlers.styleAttrChange( 'buttonBorderSize', value ) }
|
||||
isUnitSelectTabbable
|
||||
/>
|
||||
</FlexBlock>
|
||||
<FlexBlock>
|
||||
<__experimentalUnitControl
|
||||
onChange={ ( value ) => handlers.styleAttrChange( 'buttonBorderRadius', value ) }
|
||||
label={ strings.border_radius }
|
||||
min={ 0 }
|
||||
isUnitSelectTabbable
|
||||
value={ props.attributes.buttonBorderRadius } />
|
||||
</FlexBlock>
|
||||
</Flex>
|
||||
|
||||
<div className="wpforms-gutenberg-form-selector-color-picker">
|
||||
<div className="wpforms-gutenberg-form-selector-control-label">{ strings.colors }</div>
|
||||
<PanelColorSettings
|
||||
__experimentalIsRenderedInSidebar
|
||||
enableAlpha
|
||||
showTitle={ false }
|
||||
className={ formSelectorCommon.getColorPanelClass( props.attributes.buttonBorderStyle ) }
|
||||
colorSettings={ [
|
||||
{
|
||||
value: props.attributes.buttonBackgroundColor,
|
||||
onChange: ( value ) => handlers.styleAttrChange( 'buttonBackgroundColor', value ),
|
||||
label: strings.background,
|
||||
},
|
||||
{
|
||||
value: props.attributes.buttonBorderColor,
|
||||
onChange: ( value ) => handlers.styleAttrChange( 'buttonBorderColor', value ),
|
||||
label: strings.border,
|
||||
},
|
||||
{
|
||||
value: props.attributes.buttonTextColor,
|
||||
onChange: ( value ) => handlers.styleAttrChange( 'buttonTextColor', value ),
|
||||
label: strings.text,
|
||||
},
|
||||
] } />
|
||||
<div className="wpforms-gutenberg-form-selector-legend wpforms-button-color-notice">
|
||||
{ strings.button_color_notice }
|
||||
</div>
|
||||
</div>
|
||||
</PanelBody>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
return app;
|
||||
} )() );
|
||||
+2041
File diff suppressed because it is too large
Load Diff
Executable
+255
@@ -0,0 +1,255 @@
|
||||
/* global wpforms_gutenberg_form_selector */
|
||||
/* jshint es3: false, esversion: 6 */
|
||||
|
||||
/**
|
||||
* @param strings.border_color
|
||||
* @param strings.border_style
|
||||
* @param strings.border_width
|
||||
* @param strings.container_styles
|
||||
* @param strings.shadow_size
|
||||
*/
|
||||
|
||||
/**
|
||||
* Gutenberg editor block.
|
||||
*
|
||||
* Container styles panel module.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*/
|
||||
export default ( ( $ ) => {
|
||||
/**
|
||||
* WP core components.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*/
|
||||
const { PanelColorSettings } = wp.blockEditor || wp.editor;
|
||||
const { SelectControl, PanelBody, Flex, FlexBlock, __experimentalUnitControl } = wp.components;
|
||||
|
||||
/**
|
||||
* Localized data aliases.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*/
|
||||
const { strings, defaults } = wpforms_gutenberg_form_selector;
|
||||
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const app = {
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*/
|
||||
init() {
|
||||
$( app.ready );
|
||||
},
|
||||
|
||||
/**
|
||||
* Document ready.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*/
|
||||
ready() {
|
||||
app.events();
|
||||
},
|
||||
|
||||
/**
|
||||
* Events.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*/
|
||||
events() {
|
||||
},
|
||||
|
||||
/**
|
||||
* Get block attributes.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @return {Object} Block attributes.
|
||||
*/
|
||||
getBlockAttributes() {
|
||||
return {
|
||||
containerPadding: {
|
||||
type: 'string',
|
||||
default: defaults.containerPadding,
|
||||
},
|
||||
containerBorderStyle: {
|
||||
type: 'string',
|
||||
default: defaults.containerBorderStyle,
|
||||
},
|
||||
containerBorderWidth: {
|
||||
type: 'string',
|
||||
default: defaults.containerBorderWidth,
|
||||
},
|
||||
containerBorderColor: {
|
||||
type: 'string',
|
||||
default: defaults.containerBorderColor,
|
||||
},
|
||||
containerBorderRadius: {
|
||||
type: 'string',
|
||||
default: defaults.containerBorderRadius,
|
||||
},
|
||||
containerShadowSize: {
|
||||
type: 'string',
|
||||
default: defaults.containerShadowSize,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Get Container Styles panel JSX code.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @param {Object} props Block properties.
|
||||
* @param {Object} handlers Block handlers.
|
||||
* @param {Object} formSelectorCommon Common form selector functions.
|
||||
*
|
||||
* @param {Object} uiState UI state.
|
||||
*
|
||||
* @return {Object} Field styles JSX code.
|
||||
*/
|
||||
getContainerStyles( props, handlers, formSelectorCommon, uiState ) { // eslint-disable-line max-lines-per-function, complexity
|
||||
let cssClass = formSelectorCommon.getPanelClass( props );
|
||||
const isNotDisabled = uiState.isNotDisabled;
|
||||
const isProEnabled = uiState.isProEnabled;
|
||||
|
||||
if ( ! isNotDisabled ) {
|
||||
cssClass += ' wpforms-gutenberg-panel-disabled';
|
||||
}
|
||||
|
||||
return (
|
||||
<PanelBody className={ cssClass } title={ strings.container_styles }>
|
||||
<div // eslint-disable-line jsx-a11y/no-static-element-interactions
|
||||
className="wpforms-gutenberg-form-selector-panel-body"
|
||||
onClick={ ( event ) => {
|
||||
if ( isNotDisabled ) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.stopPropagation();
|
||||
|
||||
if ( ! isProEnabled ) {
|
||||
return formSelectorCommon.education.showProModal( 'container', strings.container_styles );
|
||||
}
|
||||
|
||||
formSelectorCommon.education.showLicenseModal( 'container', strings.container_styles, 'container-styles' );
|
||||
} }
|
||||
onKeyDown={ ( event ) => {
|
||||
if ( isNotDisabled ) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.stopPropagation();
|
||||
|
||||
if ( ! isProEnabled ) {
|
||||
return formSelectorCommon.education.showProModal( 'container', strings.container_styles );
|
||||
}
|
||||
|
||||
formSelectorCommon.education.showLicenseModal( 'container', strings.container_styles, 'container-styles' );
|
||||
} }
|
||||
>
|
||||
<Flex gap={ 4 } align="flex-start" className="wpforms-gutenberg-form-selector-flex" justify="space-between">
|
||||
<FlexBlock>
|
||||
<__experimentalUnitControl
|
||||
label={ strings.padding }
|
||||
tabIndex={ isNotDisabled ? 0 : -1 }
|
||||
value={ props.attributes.containerPadding }
|
||||
min={ 0 }
|
||||
isUnitSelectTabbable={ isNotDisabled }
|
||||
onChange={ ( value ) => handlers.styleAttrChange( 'containerPadding', value ) }
|
||||
/>
|
||||
</FlexBlock>
|
||||
<FlexBlock>
|
||||
<SelectControl
|
||||
label={ strings.border_style }
|
||||
tabIndex={ isNotDisabled ? 0 : -1 }
|
||||
value={ props.attributes.containerBorderStyle }
|
||||
options={ [
|
||||
{ label: strings.none, value: 'none' },
|
||||
{ label: strings.solid, value: 'solid' },
|
||||
{ label: strings.dotted, value: 'dotted' },
|
||||
{ label: strings.dashed, value: 'dashed' },
|
||||
{ label: strings.double, value: 'double' },
|
||||
] }
|
||||
onChange={ ( value ) => handlers.styleAttrChange( 'containerBorderStyle', value ) }
|
||||
/>
|
||||
</FlexBlock>
|
||||
</Flex>
|
||||
<Flex gap={ 4 } align="flex-start" className="wpforms-gutenberg-form-selector-flex" justify="space-between">
|
||||
<FlexBlock>
|
||||
<__experimentalUnitControl
|
||||
label={ strings.border_width }
|
||||
tabIndex={ isNotDisabled ? 0 : -1 }
|
||||
value={ props.attributes.containerBorderStyle === 'none' ? '' : props.attributes.containerBorderWidth }
|
||||
min={ 0 }
|
||||
disabled={ props.attributes.containerBorderStyle === 'none' }
|
||||
isUnitSelectTabbable={ isNotDisabled }
|
||||
onChange={ ( value ) => handlers.styleAttrChange( 'containerBorderWidth', value ) }
|
||||
/>
|
||||
</FlexBlock>
|
||||
<FlexBlock>
|
||||
<__experimentalUnitControl
|
||||
label={ strings.border_radius }
|
||||
tabIndex={ isNotDisabled ? 0 : -1 }
|
||||
value={ props.attributes.containerBorderRadius }
|
||||
min={ 0 }
|
||||
isUnitSelectTabbable={ isNotDisabled }
|
||||
onChange={ ( value ) => handlers.styleAttrChange( 'containerBorderRadius', value ) }
|
||||
/>
|
||||
</FlexBlock>
|
||||
</Flex>
|
||||
<Flex gap={ 4 } align="flex-start" className="wpforms-gutenberg-form-selector-flex" justify="space-between">
|
||||
<FlexBlock>
|
||||
<SelectControl
|
||||
label={ strings.shadow_size }
|
||||
tabIndex={ isNotDisabled ? 0 : -1 }
|
||||
value={ props.attributes.containerShadowSize }
|
||||
options={ [
|
||||
{ label: strings.none, value: 'none' },
|
||||
{ label: strings.small, value: 'small' },
|
||||
{ label: strings.medium, value: 'medium' },
|
||||
{ label: strings.large, value: 'large' },
|
||||
] }
|
||||
onChange={ ( value ) => handlers.styleAttrChange( 'containerShadowSize', value ) }
|
||||
/>
|
||||
</FlexBlock>
|
||||
</Flex>
|
||||
<Flex gap={ 4 } align="flex-start" className="wpforms-gutenberg-form-selector-flex" justify="space-between">
|
||||
<FlexBlock>
|
||||
<div className="wpforms-gutenberg-form-selector-control-label">{ strings.colors }</div>
|
||||
<PanelColorSettings
|
||||
__experimentalIsRenderedInSidebar
|
||||
enableAlpha
|
||||
showTitle={ false }
|
||||
tabIndex={ isNotDisabled ? 0 : -1 }
|
||||
className={ props.attributes.containerBorderStyle === 'none' ? 'wpforms-gutenberg-form-selector-color-panel wpforms-gutenberg-form-selector-color-panel-disabled' : 'wpforms-gutenberg-form-selector-color-panel' }
|
||||
colorSettings={ [
|
||||
{
|
||||
value: props.attributes.containerBorderColor,
|
||||
onChange: ( value ) => {
|
||||
if ( ! isNotDisabled ) {
|
||||
return;
|
||||
}
|
||||
handlers.styleAttrChange( 'containerBorderColor', value );
|
||||
},
|
||||
label: strings.border_color,
|
||||
},
|
||||
] }
|
||||
/>
|
||||
</FlexBlock>
|
||||
</Flex>
|
||||
</div>
|
||||
</PanelBody>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
return app;
|
||||
} )( jQuery );
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/* global wpforms_education, WPFormsEducation */
|
||||
|
||||
/**
|
||||
* WPForms Education Modal module.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*/
|
||||
export default ( ( $ ) => {
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const app = {
|
||||
/**
|
||||
* Open educational popup for users with no Pro license.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @param {string} panel Panel slug.
|
||||
* @param {string} feature Feature name.
|
||||
*/
|
||||
showProModal( panel, feature ) {
|
||||
const type = 'pro';
|
||||
const message = wpforms_education.upgrade[ type ].message_plural.replace( /%name%/g, feature );
|
||||
const utmContent = {
|
||||
container: 'Upgrade to Pro - Container Styles',
|
||||
background: 'Upgrade to Pro - Background Styles',
|
||||
themes: 'Upgrade to Pro - Themes',
|
||||
};
|
||||
|
||||
$.alert( {
|
||||
backgroundDismiss: true,
|
||||
title: feature + ' ' + wpforms_education.upgrade[ type ].title_plural,
|
||||
icon: 'fa fa-lock',
|
||||
content: message,
|
||||
boxWidth: '550px',
|
||||
theme: 'modern,wpforms-education',
|
||||
closeIcon: true,
|
||||
onOpenBefore: function() { // eslint-disable-line object-shorthand
|
||||
this.$btnc.after( '<div class="discount-note">' + wpforms_education.upgrade_bonus + '</div>' );
|
||||
this.$btnc.after( wpforms_education.upgrade[ type ].doc.replace( /%25name%25/g, 'AP - ' + feature ) );
|
||||
this.$body.find( '.jconfirm-content' ).addClass( 'lite-upgrade' );
|
||||
},
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: wpforms_education.upgrade[ type ].button,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
action: () => {
|
||||
window.open( WPFormsEducation.core.getUpgradeURL( utmContent[ panel ], type ), '_blank' );
|
||||
WPFormsEducation.core.upgradeModalThankYou( type );
|
||||
},
|
||||
},
|
||||
},
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Open license modal.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @param {string} feature Feature name.
|
||||
* @param {string} fieldName Field name.
|
||||
* @param {string} utmContent UTM content.
|
||||
*/
|
||||
showLicenseModal( feature, fieldName, utmContent ) {
|
||||
WPFormsEducation.proCore.licenseModal( feature, fieldName, utmContent );
|
||||
},
|
||||
};
|
||||
|
||||
return app;
|
||||
} )( jQuery );
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
/* global wpforms_gutenberg_form_selector */
|
||||
/* jshint es3: false, esversion: 6 */
|
||||
|
||||
/**
|
||||
* @param strings.field_styles
|
||||
* @param strings.lead_forms_panel_notice_head
|
||||
* @param strings.lead_forms_panel_notice_text
|
||||
* @param strings.learn_more
|
||||
* @param strings.use_modern_notice_head
|
||||
* @param strings.use_modern_notice_link
|
||||
* @param strings.use_modern_notice_text
|
||||
*/
|
||||
|
||||
/**
|
||||
* Gutenberg editor block.
|
||||
*
|
||||
* Field styles panel module.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*/
|
||||
export default ( ( function() {
|
||||
/**
|
||||
* WP core components.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*/
|
||||
const { PanelColorSettings } = wp.blockEditor || wp.editor;
|
||||
const { SelectControl, PanelBody, Flex, FlexBlock, __experimentalUnitControl } = wp.components;
|
||||
|
||||
/**
|
||||
* Localized data aliases.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*/
|
||||
const { strings, defaults } = wpforms_gutenberg_form_selector;
|
||||
|
||||
// noinspection UnnecessaryLocalVariableJS
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const app = {
|
||||
/**
|
||||
* Get block attributes.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @return {Object} Block attributes.
|
||||
*/
|
||||
getBlockAttributes() {
|
||||
return {
|
||||
fieldSize: {
|
||||
type: 'string',
|
||||
default: defaults.fieldSize,
|
||||
},
|
||||
fieldBorderStyle: {
|
||||
type: 'string',
|
||||
default: defaults.fieldBorderStyle,
|
||||
},
|
||||
fieldBorderSize: {
|
||||
type: 'string',
|
||||
default: defaults.fieldBorderSize,
|
||||
},
|
||||
fieldBorderRadius: {
|
||||
type: 'string',
|
||||
default: defaults.fieldBorderRadius,
|
||||
},
|
||||
fieldBackgroundColor: {
|
||||
type: 'string',
|
||||
default: defaults.fieldBackgroundColor,
|
||||
},
|
||||
fieldBorderColor: {
|
||||
type: 'string',
|
||||
default: defaults.fieldBorderColor,
|
||||
},
|
||||
fieldTextColor: {
|
||||
type: 'string',
|
||||
default: defaults.fieldTextColor,
|
||||
},
|
||||
fieldMenuColor: {
|
||||
type: 'string',
|
||||
default: defaults.fieldMenuColor,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Get Field styles JSX code.
|
||||
*
|
||||
* @since 1.8.8
|
||||
*
|
||||
* @param {Object} props Block properties.
|
||||
* @param {Object} handlers Block event handlers.
|
||||
* @param {Object} sizeOptions Size selector options.
|
||||
* @param {Object} formSelectorCommon Form selector common object.
|
||||
*
|
||||
* @return {Object} Field styles JSX code.
|
||||
*/
|
||||
getFieldStyles( props, handlers, sizeOptions, formSelectorCommon ) { // eslint-disable-line max-lines-per-function
|
||||
return (
|
||||
<PanelBody className={ formSelectorCommon.getPanelClass( props ) } title={ strings.field_styles }>
|
||||
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
|
||||
<FlexBlock>
|
||||
<SelectControl
|
||||
label={ strings.size }
|
||||
value={ props.attributes.fieldSize }
|
||||
options={ sizeOptions }
|
||||
onChange={ ( value ) => handlers.styleAttrChange( 'fieldSize', value ) }
|
||||
/>
|
||||
</FlexBlock>
|
||||
<FlexBlock>
|
||||
<SelectControl
|
||||
label={ strings.border }
|
||||
value={ props.attributes.fieldBorderStyle }
|
||||
options={
|
||||
[
|
||||
{ label: strings.none, value: 'none' },
|
||||
{ label: strings.solid, value: 'solid' },
|
||||
{ label: strings.dashed, value: 'dashed' },
|
||||
{ label: strings.dotted, value: 'dotted' },
|
||||
]
|
||||
}
|
||||
onChange={ ( value ) => handlers.styleAttrChange( 'fieldBorderStyle', value ) }
|
||||
/>
|
||||
</FlexBlock>
|
||||
</Flex>
|
||||
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
|
||||
<FlexBlock>
|
||||
<__experimentalUnitControl
|
||||
label={ strings.border_size }
|
||||
value={ props.attributes.fieldBorderStyle === 'none' ? '' : props.attributes.fieldBorderSize }
|
||||
min={ 0 }
|
||||
disabled={ props.attributes.fieldBorderStyle === 'none' }
|
||||
onChange={ ( value ) => handlers.styleAttrChange( 'fieldBorderSize', value ) }
|
||||
isUnitSelectTabbable
|
||||
/>
|
||||
</FlexBlock>
|
||||
<FlexBlock>
|
||||
<__experimentalUnitControl
|
||||
label={ strings.border_radius }
|
||||
value={ props.attributes.fieldBorderRadius }
|
||||
min={ 0 }
|
||||
isUnitSelectTabbable
|
||||
onChange={ ( value ) => handlers.styleAttrChange( 'fieldBorderRadius', value ) }
|
||||
/>
|
||||
</FlexBlock>
|
||||
</Flex>
|
||||
|
||||
<div className="wpforms-gutenberg-form-selector-color-picker">
|
||||
<div className="wpforms-gutenberg-form-selector-control-label">{ strings.colors }</div>
|
||||
<PanelColorSettings
|
||||
__experimentalIsRenderedInSidebar
|
||||
enableAlpha
|
||||
showTitle={ false }
|
||||
className={ formSelectorCommon.getColorPanelClass( props.attributes.fieldBorderStyle ) }
|
||||
colorSettings={ [
|
||||
{
|
||||
value: props.attributes.fieldBackgroundColor,
|
||||
onChange: ( value ) => handlers.styleAttrChange( 'fieldBackgroundColor', value ),
|
||||
label: strings.background,
|
||||
},
|
||||
{
|
||||
value: props.attributes.fieldBorderColor,
|
||||
onChange: ( value ) => handlers.styleAttrChange( 'fieldBorderColor', value ),
|
||||
label: strings.border,
|
||||
},
|
||||
{
|
||||
value: props.attributes.fieldTextColor,
|
||||
onChange: ( value ) => handlers.styleAttrChange( 'fieldTextColor', value ),
|
||||
label: strings.text,
|
||||
},
|
||||
{
|
||||
value: props.attributes.fieldMenuColor,
|
||||
onChange: ( value ) => handlers.styleAttrChange( 'fieldMenuColor', value ),
|
||||
label: strings.menu,
|
||||
},
|
||||
] }
|
||||
/>
|
||||
</div>
|
||||
</PanelBody>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
return app;
|
||||
} )() );
|
||||
+1044
File diff suppressed because it is too large
Load Diff
+416
@@ -0,0 +1,416 @@
|
||||
/* global wpforms_builder, WPFormsBuilderPaymentsUtils */
|
||||
|
||||
/**
|
||||
* WPForms Square builder function.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
const WPFormsBuilderSquare = window.WPFormsBuilderSquare || ( function( document, window, $ ) {
|
||||
/**
|
||||
* Elements holder.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const el = {};
|
||||
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const app = {
|
||||
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
init() {
|
||||
$( app.ready );
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialized once the DOM and Providers are fully loaded.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
ready() {
|
||||
// Cache DOM elements.
|
||||
el.$singlePaymentControl = $( '#wpforms-panel-field-square-enable_one_time' );
|
||||
el.$recurringPaymentControl = $( '#wpforms-panel-field-square-enable_recurring' );
|
||||
el.$panelContent = $( '#wpforms-panel-content-section-payment-square' );
|
||||
el.$AJAXSubmitOption = $( '#wpforms-panel-field-settings-ajax_submit' );
|
||||
el.$cardButton = $( '#wpforms-add-fields-square' );
|
||||
el.$alert = $( '#wpforms-square-credit-card-alert' );
|
||||
el.$feeNotice = $( '.wpforms-square-notice-info' );
|
||||
|
||||
app.bindUIActions();
|
||||
app.bindPlanUIActions();
|
||||
|
||||
if ( ! wpforms_builder.square_is_pro ) {
|
||||
const baseSelector = '.wpforms-panel-content-section-square',
|
||||
toggleInput = `${ baseSelector } .wpforms-panel-content-section-payment-toggle input`,
|
||||
planNameInput = `${ baseSelector } .wpforms-panel-content-section-payment-plan-name input`;
|
||||
|
||||
$( toggleInput ).each( WPFormsBuilderPaymentsUtils.toggleContent );
|
||||
$( planNameInput ).each( WPFormsBuilderPaymentsUtils.checkPlanName );
|
||||
|
||||
$( '#wpforms-panel-payments' )
|
||||
.on( 'click', toggleInput, WPFormsBuilderPaymentsUtils.toggleContent )
|
||||
.on( 'click', `${ baseSelector } .wpforms-panel-content-section-payment-plan-head-buttons-toggle`, WPFormsBuilderPaymentsUtils.togglePlan )
|
||||
.on( 'click', `${ baseSelector } .wpforms-panel-content-section-payment-plan-head-buttons-delete`, WPFormsBuilderPaymentsUtils.deletePlan )
|
||||
.on( 'input', planNameInput, WPFormsBuilderPaymentsUtils.renamePlan )
|
||||
.on( 'focusout', planNameInput, WPFormsBuilderPaymentsUtils.checkPlanName );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Process various events.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
bindUIActions() {
|
||||
$( document ).on( 'wpformsSaved', app.ajaxRequiredCheck )
|
||||
.on( 'wpformsSaved', app.paymentsEnabledCheck )
|
||||
.on( 'wpformsSaved', app.requiredFieldsCheck )
|
||||
.on( 'wpformsFieldAdd', app.fieldAdded )
|
||||
.on( 'wpformsFieldDelete', app.fieldDeleted )
|
||||
.on( 'wpformsPaymentsPlanCreated', app.toggleMultiplePlansWarning )
|
||||
.on( 'wpformsPaymentsPlanCreated', app.bindPlanUIActions )
|
||||
.on( 'wpformsPaymentsPlanDeleted', app.toggleMultiplePlansWarning );
|
||||
|
||||
el.$cardButton.on( 'click', app.connectionCheck );
|
||||
},
|
||||
|
||||
/**
|
||||
* Bind plan UI actions.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
bindPlanUIActions() {
|
||||
el.$panelContent.find( '.wpforms-panel-content-section-payment-plan-body .wpforms-panel-field-select select' ).on( 'change', app.resetRequiredPlanFieldError );
|
||||
},
|
||||
|
||||
/**
|
||||
* Notify user if AJAX submission is not required.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
ajaxRequiredCheck() {
|
||||
if ( ! $( '#wpforms-panel-fields .wpforms-field.wpforms-field-square' ).length ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( app.isAJAXSubmitEnabled() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.alert( {
|
||||
title: wpforms_builder.heads_up,
|
||||
content: wpforms_builder.square_ajax_required,
|
||||
icon: 'fa fa-exclamation-circle',
|
||||
type: 'orange',
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: wpforms_builder.ok,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
},
|
||||
},
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Notify user if Square Payments are not enabled.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
paymentsEnabledCheck() {
|
||||
if ( ! $( '#wpforms-panel-fields .wpforms-field.wpforms-field-square' ).length ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( app.isPaymentsEnabled() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.alert( {
|
||||
title: wpforms_builder.heads_up,
|
||||
content: wpforms_builder.square_payments_enabled_required,
|
||||
icon: 'fa fa-exclamation-circle',
|
||||
type: 'red',
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: wpforms_builder.ok,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
},
|
||||
},
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* On form save notify users about required fields.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
requiredFieldsCheck() {
|
||||
if ( ! el.$recurringPaymentControl.is( ':checked' ) || el.$panelContent.hasClass( 'wpforms-hidden' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
let showAlert = false;
|
||||
|
||||
el.$panelContent.find( '.wpforms-panel-content-section-payment-plan' ).each( function() {
|
||||
const $plan = $( this ),
|
||||
planId = $plan.data( 'plan-id' ),
|
||||
$emailField = $( `#wpforms-panel-field-square-recurring-${ planId }-customer_email` ),
|
||||
$nameField = $( `#wpforms-panel-field-square-recurring-${ planId }-customer_name` );
|
||||
|
||||
if (
|
||||
! $emailField.val()
|
||||
) {
|
||||
$emailField.addClass( 'wpforms-required-field-error' );
|
||||
showAlert = true;
|
||||
}
|
||||
|
||||
if (
|
||||
! $nameField.val()
|
||||
) {
|
||||
$nameField.addClass( 'wpforms-required-field-error' );
|
||||
showAlert = true;
|
||||
}
|
||||
} );
|
||||
|
||||
if ( ! showAlert ) {
|
||||
return;
|
||||
}
|
||||
|
||||
let alertMessage = wpforms_builder.square_recurring_payments_fields_required;
|
||||
|
||||
if ( ! $( '.wpforms-panel-content-section-square' ).is( ':visible' ) ) {
|
||||
alertMessage += ' ' + wpforms_builder.square_recurring_payments_fields_settings;
|
||||
}
|
||||
|
||||
$.alert( {
|
||||
title: wpforms_builder.square_recurring_payments_fields_heading,
|
||||
content: alertMessage,
|
||||
icon: 'fa fa-exclamation-circle',
|
||||
type: 'red',
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: wpforms_builder.ok,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
},
|
||||
},
|
||||
onOpen() {
|
||||
$( '.wpforms-square-settings-redirect' ).on( 'click', app.settingsRedirect );
|
||||
},
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Redirect to the settings tab.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
settingsRedirect() {
|
||||
// Open the Square settings tab.
|
||||
$( '.wpforms-panel-payments-button' ).trigger( 'click' );
|
||||
$( '.wpforms-panel-sidebar-section-square' ).trigger( 'click' );
|
||||
|
||||
// Scroll to the Stripe settings.
|
||||
window.location.href = window.location.pathname + window.location.search + '#wpforms-panel-field-square-enable_recurring-wrap';
|
||||
|
||||
// Close the alert.
|
||||
$( this ).closest( '.jconfirm-box' ).find( '.btn-confirm' ).trigger( 'click' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Maybe reset required recurring field error class.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
resetRequiredPlanFieldError() {
|
||||
const $nameAttr = $( this ).attr( 'name' );
|
||||
|
||||
if ( ! $nameAttr.includes( 'customer_email' ) && ! $nameAttr.includes( 'customer_name' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$( this ).toggleClass( 'wpforms-required-field-error', ! $( this ).val() );
|
||||
},
|
||||
|
||||
// eslint-disable-next-line jsdoc/require-returns-check
|
||||
/**
|
||||
* Notify user if Square connection are missing.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @return {boolean} False if button clicks should be prevented.
|
||||
*/
|
||||
connectionCheck() {
|
||||
if ( $( this ).hasClass( 'wpforms-add-fields-button-disabled' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! $( this ).hasClass( 'square-connection-required' ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$.alert( {
|
||||
title: wpforms_builder.heads_up,
|
||||
content: wpforms_builder.square_connection_required,
|
||||
icon: 'fa fa-exclamation-circle',
|
||||
type: 'orange',
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: wpforms_builder.ok,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
},
|
||||
},
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* We have to do several actions when the "Square" field is added.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {Object} e Event object.
|
||||
* @param {number} id Field ID.
|
||||
* @param {string} type Field type.
|
||||
*/
|
||||
fieldAdded( e, id, type ) {
|
||||
if ( type === 'square' ) {
|
||||
app.cardButtonToggle( true );
|
||||
app.settingsToggle( true );
|
||||
app.paymentsEnabledCheck();
|
||||
el.$feeNotice.toggleClass( 'wpforms-hidden' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* We have to do several actions for UI when the "Square" credit card field is deleted.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {Object} e Event object.
|
||||
* @param {number} id Field ID.
|
||||
* @param {string} type Field type.
|
||||
*/
|
||||
fieldDeleted( e, id, type ) {
|
||||
if ( type === 'square' ) {
|
||||
app.cardButtonToggle( false );
|
||||
app.settingsToggle( false );
|
||||
app.disablePayments();
|
||||
app.disableNotifications();
|
||||
el.$feeNotice.toggleClass( 'wpforms-hidden' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggles visibility of multiple plans warning.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
toggleMultiplePlansWarning() {
|
||||
el.$panelContent.find( '.wpforms-square-multiple-plans-warning' ).toggleClass( 'wpforms-hidden', el.$panelContent.find( '.wpforms-panel-content-section-payment-plan' ).length === 1 );
|
||||
},
|
||||
|
||||
/**
|
||||
* Enable or disable the "Square" field in the fields list.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {boolean} isDisabled If true then a card button will be disabled.
|
||||
*/
|
||||
cardButtonToggle( isDisabled ) {
|
||||
el.$cardButton
|
||||
.prop( 'disabled', isDisabled )
|
||||
.toggleClass( 'wpforms-add-fields-button-disabled', isDisabled );
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggle visibility of the Square payment settings.
|
||||
*
|
||||
* If the "Square" field has been added then reveal the settings,
|
||||
* otherwise hide them.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {boolean} display Show or hide settings.
|
||||
*/
|
||||
settingsToggle( display ) {
|
||||
if ( ! el.$alert.length ) {
|
||||
return;
|
||||
}
|
||||
|
||||
el.$alert.toggleClass( 'wpforms-hidden', display );
|
||||
$( '#wpforms-panel-content-section-payment-square' ).toggleClass( 'wpforms-hidden', ! display );
|
||||
|
||||
// Uncheck the Payments > Square > Enable Square Payments setting.
|
||||
if ( ! display ) {
|
||||
el.$singlePaymentControl.prop( 'checked', false ).trigger( 'change' );
|
||||
el.$recurringPaymentControl.prop( 'checked', false ).trigger( 'change' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Make sure that "One-Time Payments" and "Recurring Payments" toggles are turned off.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
disablePayments() {
|
||||
const toggleInput = $( '#wpforms-panel-field-square-enable_one_time, #wpforms-panel-field-square-enable_recurring' );
|
||||
|
||||
toggleInput.prop( 'checked', false ).trigger( 'change' ).each( WPFormsBuilderPaymentsUtils.toggleContent );
|
||||
},
|
||||
|
||||
/**
|
||||
* Disable notifications.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
disableNotifications() {
|
||||
const $notificationWrap = $( '.wpforms-panel-content-section-notifications [id*="-square-wrap"]' );
|
||||
|
||||
$notificationWrap.find( 'input[id*="-square"]' ).prop( 'checked', false );
|
||||
$notificationWrap.addClass( 'wpforms-hidden' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine whether payments are enabled in the Payments > Square panel.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @return {boolean} Payments are enabled.
|
||||
*/
|
||||
isPaymentsEnabled() {
|
||||
return el.$singlePaymentControl.is( ':checked' ) || el.$recurringPaymentControl.is( ':checked' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine whether AJAX form submission is enabled in the Settings > General.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @return {boolean} AJAX form submission is enabled.
|
||||
*/
|
||||
isAJAXSubmitEnabled() {
|
||||
return el.$AJAXSubmitOption.is( ':checked' );
|
||||
},
|
||||
};
|
||||
|
||||
// Provide access to public functions/properties.
|
||||
return app;
|
||||
}( document, window, jQuery ) );
|
||||
|
||||
// Initialize.
|
||||
WPFormsBuilderSquare.init();
|
||||
Vendored
Executable
+1
File diff suppressed because one or more lines are too long
+375
@@ -0,0 +1,375 @@
|
||||
/* global wpforms_admin, WPFormsAdmin, wpf */
|
||||
|
||||
/**
|
||||
* WPForms Square settings function.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
const WPFormsSettingsSquare = window.WPFormsSettingsSquare || ( function( document, window, $ ) {
|
||||
/**
|
||||
* Elements.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const $el = {
|
||||
sandboxModeCheckbox: $( '#wpforms-setting-square-sandbox-mode' ),
|
||||
sandboxConnectionStatusBlock: $( '#wpforms-setting-row-square-connection-status-sandbox' ),
|
||||
productionConnectionStatusBlock: $( '#wpforms-setting-row-square-connection-status-production' ),
|
||||
sandboxLocationBlock: $( '#wpforms-setting-row-square-location-id-sandbox' ),
|
||||
sandboxLocationStatusBlock: $( '#wpforms-setting-row-square-location-status-sandbox' ),
|
||||
productionLocationBlock: $( '#wpforms-setting-row-square-location-id-production' ),
|
||||
productionLocationStatusBlock: $( '#wpforms-setting-row-square-location-status-production' ),
|
||||
refreshBtn: $( '.wpforms-square-refresh-btn' ),
|
||||
copyButton: $( '#wpforms-setting-row-square-webhooks-endpoint-set .wpforms-copy-to-clipboard' ),
|
||||
webhooksEnableCheckbox: $( '#wpforms-setting-square-webhooks-enabled' ),
|
||||
webhookEndpointUrl: $( 'input#wpforms-square-webhook-endpoint-url' ),
|
||||
webhookMethod: $( 'input[name="square-webhooks-communication"]' ),
|
||||
webhookCommunicationStatusNotice: $( '#wpforms-setting-row-square-webhooks-communication-status' ),
|
||||
webhookConnectBtn: $( '#wpforms-setting-square-webhooks-connect' ),
|
||||
webhookConnectRow: $( '#wpforms-setting-row-square-webhooks-connect' ),
|
||||
webhookConnectStatusRow: $( '#wpforms-setting-row-square-webhooks-connect-status-production, #wpforms-setting-row-square-webhooks-connect-status-sandbox' ),
|
||||
};
|
||||
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const app = {
|
||||
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
init() {
|
||||
$( app.ready );
|
||||
},
|
||||
|
||||
/**
|
||||
* Document ready.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
ready() {
|
||||
app.events();
|
||||
},
|
||||
|
||||
/**
|
||||
* Register JS events.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
events() {
|
||||
$el.sandboxModeCheckbox.on( 'change', app.credentialsFieldsDisplay );
|
||||
$el.refreshBtn.on( 'click', app.refreshTokensCallback );
|
||||
$el.webhooksEnableCheckbox.on( 'change', app.webhooksEnableCallback );
|
||||
$el.webhookConnectBtn.on( 'click', app.modals.displayWebhookConfigPopup );
|
||||
$el.webhookMethod.on( 'change', app.updateWebhookEndpointUrl );
|
||||
$el.copyButton.on( 'click', function( e ) {
|
||||
wpf.copyValueToClipboard( e, $( this ), $el.webhookEndpointUrl );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the endpoint URL.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
updateWebhookEndpointUrl() {
|
||||
const checked = $el.webhookMethod.filter( ':checked' ).val(),
|
||||
newUrl = wpforms_admin.square.webhook_urls[ checked ];
|
||||
|
||||
$el.webhookEndpointUrl.val( newUrl );
|
||||
$el.webhookCommunicationStatusNotice.removeClass( 'wpforms-hide' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Enable webhooks.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
webhooksEnableCallback() {
|
||||
$el.webhookConnectRow.toggleClass( 'wpforms-hide', ! $( this ).is( ':checked' ) );
|
||||
$el.webhookConnectStatusRow.toggleClass( 'wpforms-hide', ! $( this ).is( ':checked' ) );
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a webhook.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {string} token Personal access token.
|
||||
*
|
||||
* @return {Promise} Promise an object.
|
||||
*/
|
||||
createWebhook( token ) {
|
||||
return new Promise( ( resolve, reject ) => {
|
||||
$.ajax( {
|
||||
url: wpforms_admin.ajax_url,
|
||||
type: 'post',
|
||||
dataType: 'json',
|
||||
data: {
|
||||
action: 'wpforms_square_create_webhook',
|
||||
nonce: wpforms_admin.nonce,
|
||||
token,
|
||||
},
|
||||
success( response ) {
|
||||
if ( response.success ) {
|
||||
resolve( response );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
reject( response );
|
||||
},
|
||||
error() {
|
||||
reject( { success: false, message: 'An error occurred.' } );
|
||||
},
|
||||
} );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Refresh tokens.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
refreshTokensCallback() {
|
||||
const $btn = $( this );
|
||||
const buttonWidth = $btn.outerWidth();
|
||||
const buttonLabel = $btn.text();
|
||||
const settings = {
|
||||
url: wpforms_admin.ajax_url,
|
||||
type: 'post',
|
||||
dataType: 'json',
|
||||
data: {
|
||||
action: 'wpforms_square_refresh_connection',
|
||||
nonce: wpforms_admin.nonce,
|
||||
mode: $btn.data( 'mode' ),
|
||||
},
|
||||
beforeSend() {
|
||||
$btn.css( 'width', buttonWidth ).html( WPFormsAdmin.settings.iconSpinner ).prop( 'disabled', true );
|
||||
},
|
||||
};
|
||||
|
||||
let errorMessage = wpforms_admin.square.refresh_error;
|
||||
|
||||
// Perform an Ajax request.
|
||||
$.ajax( settings )
|
||||
.done( function( response ) {
|
||||
if ( response.success ) {
|
||||
$btn
|
||||
.css( 'pointerEvents', 'none' )
|
||||
.removeClass( 'wpforms-btn-light-grey' )
|
||||
.addClass( 'wpforms-btn-grey' )
|
||||
.html( 'Refreshed!' );
|
||||
|
||||
$btn.closest( 'form' ).css( 'cursor', 'wait' );
|
||||
|
||||
window.location = $btn.data( 'url' );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call( response, 'data' ) &&
|
||||
response.data !== ''
|
||||
) {
|
||||
errorMessage = response.data;
|
||||
}
|
||||
|
||||
$btn
|
||||
.css( 'width', 'auto' )
|
||||
.html( buttonLabel )
|
||||
.prop( 'disabled', false );
|
||||
app.modals.refreshTokensError( errorMessage );
|
||||
} )
|
||||
.fail( function() {
|
||||
$btn
|
||||
.css( 'width', 'auto' )
|
||||
.html( buttonLabel )
|
||||
.prop( 'disabled', false );
|
||||
app.modals.refreshTokensError( errorMessage );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Conditionally show Square mode switch warning.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
credentialsFieldsDisplay() {
|
||||
const sandboxModeEnabled = $el.sandboxModeCheckbox.is( ':checked' );
|
||||
|
||||
if ( sandboxModeEnabled ) {
|
||||
$el.sandboxConnectionStatusBlock.show();
|
||||
$el.sandboxLocationBlock.show();
|
||||
$el.sandboxLocationStatusBlock.show();
|
||||
|
||||
$el.productionConnectionStatusBlock.hide();
|
||||
$el.productionLocationBlock.hide();
|
||||
$el.productionLocationStatusBlock.hide();
|
||||
} else {
|
||||
$el.sandboxConnectionStatusBlock.hide();
|
||||
$el.sandboxLocationBlock.hide();
|
||||
$el.sandboxLocationStatusBlock.hide();
|
||||
|
||||
$el.productionConnectionStatusBlock.show();
|
||||
$el.productionLocationBlock.show();
|
||||
$el.productionLocationStatusBlock.show();
|
||||
}
|
||||
|
||||
if ( sandboxModeEnabled && $el.sandboxConnectionStatusBlock.find( '.wpforms-square-connected' ).length ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! sandboxModeEnabled && $el.productionConnectionStatusBlock.find( '.wpforms-square-connected' ).length ) {
|
||||
return;
|
||||
}
|
||||
|
||||
app.modals.modeChangedWarning();
|
||||
},
|
||||
|
||||
/**
|
||||
* Modals.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
modals: {
|
||||
|
||||
/**
|
||||
* Show the warning modal when Square mode is changed.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
modeChangedWarning() {
|
||||
$.alert( {
|
||||
title: wpforms_admin.heads_up,
|
||||
content: wpforms_admin.square.mode_update,
|
||||
icon: 'fa fa-exclamation-circle',
|
||||
type: 'orange',
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: wpforms_admin.ok,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
},
|
||||
},
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Refresh tokens error handling.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {string} error Error message.
|
||||
*/
|
||||
refreshTokensError( error ) {
|
||||
$.alert( {
|
||||
title: false,
|
||||
content: error,
|
||||
icon: 'fa fa-exclamation-circle',
|
||||
type: 'orange',
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: wpforms_admin.ok,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
},
|
||||
},
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Show popup with the ability to register a new webhook route or retrieve existing one.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
// eslint-disable-next-line max-lines-per-function
|
||||
displayWebhookConfigPopup() {
|
||||
$.confirm( {
|
||||
title: wpforms_admin.square.webhook_create_title,
|
||||
content: wpforms_admin.square.webhook_create_description +
|
||||
'<input type="text" id="wpforms-square-personal-access-token" placeholder="' + wpforms_admin.square.webhook_token_placeholder + '" value="">' +
|
||||
'<p class="wpforms-square-webhooks-connect-error error" style="display:none;">' + wpforms_admin.square.token_is_required + '</p>',
|
||||
icon: 'fa fa-info-circle',
|
||||
type: 'blue',
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: wpforms_admin.ok,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
action() {
|
||||
const modal = this;
|
||||
const tokenField = modal.$content.find( '#wpforms-square-personal-access-token' );
|
||||
const errorMsg = modal.$content.find( '.error' );
|
||||
const token = tokenField.val().trim();
|
||||
const title = modal.$title;
|
||||
|
||||
// Disable the button to prevent multiple clicks.
|
||||
$el.webhookConnectBtn.addClass( 'inactive' );
|
||||
|
||||
// Reset error message before validation
|
||||
errorMsg.hide().text( '' );
|
||||
|
||||
if ( token === '' ) {
|
||||
errorMsg.text( wpforms_admin.square.token_is_required ).show();
|
||||
return false; // Prevent modal from closing.
|
||||
}
|
||||
|
||||
// Show loading indicator.
|
||||
modal.buttons.confirm.setText( wpforms_admin.loading );
|
||||
modal.buttons.confirm.disable();
|
||||
|
||||
// Call API.
|
||||
app.createWebhook( token )
|
||||
.then( ( response ) => {
|
||||
modal.setContent( '<p>' + response.data.message + '</p>' );
|
||||
// Hide OK button and rename Cancel to Close.
|
||||
modal.buttons.confirm.hide();
|
||||
title.text( '' ).hide();
|
||||
modal.buttons.cancel.setText( wpforms_admin.close );
|
||||
|
||||
// Ensure user can manually close the modal.
|
||||
modal.buttons.cancel.action = function() {
|
||||
window.location.reload();
|
||||
};
|
||||
} )
|
||||
.catch( ( responseError ) => {
|
||||
errorMsg.text( responseError.data.message ).show();
|
||||
|
||||
// Re-enable confirm button for retrying.
|
||||
modal.buttons.confirm.setText( wpforms_admin.ok );
|
||||
modal.buttons.confirm.enable();
|
||||
} );
|
||||
|
||||
return false; // Prevent modal from closing immediately.
|
||||
},
|
||||
},
|
||||
cancel: {
|
||||
text: wpforms_admin.cancel,
|
||||
action() {
|
||||
// Re-enable the button.
|
||||
$el.webhookConnectBtn.removeClass( 'inactive' );
|
||||
|
||||
this.close();
|
||||
},
|
||||
},
|
||||
},
|
||||
} );
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Provide access to public functions/properties.
|
||||
return app;
|
||||
}( document, window, jQuery ) );
|
||||
|
||||
// Initialize.
|
||||
WPFormsSettingsSquare.init();
|
||||
Vendored
Executable
+1
File diff suppressed because one or more lines are too long
+807
@@ -0,0 +1,807 @@
|
||||
/* global Square, wpforms, wpforms_settings, wpforms_square, WPForms, WPFormsUtils */
|
||||
|
||||
/**
|
||||
* WPForms Square function.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
const WPFormsSquare = window.WPFormsSquare || ( function( document, window, $ ) {
|
||||
/**
|
||||
* Holder for original form submit handler.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @type {Function}
|
||||
*/
|
||||
let originalSubmitHandler;
|
||||
|
||||
/**
|
||||
* Credit card data.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const cardData = {
|
||||
cardNumber: {
|
||||
empty: true,
|
||||
valid: false,
|
||||
},
|
||||
expirationDate: {
|
||||
empty: true,
|
||||
valid: false,
|
||||
},
|
||||
cvv: {
|
||||
empty: true,
|
||||
valid: false,
|
||||
},
|
||||
postalCode: {
|
||||
empty: true,
|
||||
valid: false,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const app = {
|
||||
|
||||
/**
|
||||
* Square payments object.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
payments: null,
|
||||
|
||||
/**
|
||||
* Number of page locked to switch.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
lockedPageToSwitch: 0,
|
||||
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
init() {
|
||||
app.payments = app.getPaymentsInstance();
|
||||
|
||||
// Bail if a Square payments object isn't initialized.
|
||||
if ( app.payments === null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$( document )
|
||||
.on( 'wpformsReady', app.setupForms )
|
||||
.on( 'wpformsBeforePageChange', app.pageChange )
|
||||
.on( 'wpformsPageChange', app.afterPageChange )
|
||||
.on( 'wpformsProcessConditionalsField', app.conditionalLogicHandler );
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup and configure Square forms.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
setupForms() {
|
||||
if ( typeof $.fn.validate === 'undefined' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$( '.wpforms-square form' )
|
||||
.filter( ( _, form ) => typeof $( form ).data( 'formid' ) === 'number' ) // filter out forms which are locked (formid changed to 'locked-...').
|
||||
.each( app.updateSubmitHandler );
|
||||
},
|
||||
|
||||
/**
|
||||
* Update submitHandler for the forms containing Square.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
async updateSubmitHandler() {
|
||||
const $form = $( this );
|
||||
const validator = $form.data( 'validator' );
|
||||
|
||||
if ( ! validator || $form.hasClass( 'wpforms-square-initialization' ) || $form.hasClass( 'wpforms-square-initialized' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if the form is inside the "raw" elementor popup, we should not initialize the Square and wait for the popup to be opened.
|
||||
if ( $form.closest( '.elementor-location-popup' ).length && ! $form.closest( '.elementor-popup-modal' ).length ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$form.addClass( 'wpforms-square-initialization' );
|
||||
|
||||
// Store the original submitHandler.
|
||||
originalSubmitHandler = validator.settings.submitHandler;
|
||||
|
||||
// Replace the default submit handler.
|
||||
validator.settings.submitHandler = app.submitHandler;
|
||||
|
||||
// Get a new Card object.
|
||||
await app.getCardInstance( $form );
|
||||
},
|
||||
|
||||
/**
|
||||
* Trigger resize event if Square field has been conditionally unhidden.
|
||||
*
|
||||
* Allows Square Card field to resize itself (fixes the issue with doubled field height on some screen resolutions).
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {Event} e Event.
|
||||
* @param {number} formID Form ID.
|
||||
* @param {number} fieldID Field ID.
|
||||
* @param {boolean} pass Pass condition logic.
|
||||
* @param {string} action Action name.
|
||||
*/
|
||||
conditionalLogicHandler( e, formID, fieldID, pass, action ) {
|
||||
if ( ! app.isVisibleField( pass, action ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const el = document.getElementById( 'wpforms-' + formID + '-field_' + fieldID );
|
||||
|
||||
if ( ! el || ! el.classList.contains( 'wpforms-field-square-cardnumber' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.dispatchEvent( new Event( 'resize' ) );
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine if the field is visible after being triggered by Conditional Logic.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {boolean} pass Pass condition logic.
|
||||
* @param {string} action Action name.
|
||||
*
|
||||
* @return {boolean} The field is visible.
|
||||
*/
|
||||
isVisibleField( pass, action ) {
|
||||
return ( action === 'show' && pass ) || ( action === 'hide' && ! pass );
|
||||
},
|
||||
|
||||
/**
|
||||
* Update submitHandler for forms containing Square.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {Object} form JS form element.
|
||||
*/
|
||||
submitHandler( form ) {
|
||||
const $form = $( form );
|
||||
const validator = $form.data( 'validator' );
|
||||
const validForm = validator.form();
|
||||
const card = $form.find( '.wpforms-square-credit-card-hidden-input' ).data( 'square-card' );
|
||||
|
||||
if ( ! validForm || typeof card === 'undefined' || ! app.isProcessedCard( $form ) ) {
|
||||
originalSubmitHandler( $form );
|
||||
return;
|
||||
}
|
||||
|
||||
app.tokenize( $form, card );
|
||||
},
|
||||
|
||||
/**
|
||||
* Tokenize a card payment.
|
||||
*
|
||||
* @param {jQuery} $form Form element.
|
||||
* @param {Object} card Square Card object.
|
||||
*/
|
||||
async tokenize( $form, card ) {
|
||||
app.disableSubmitBtn( $form );
|
||||
|
||||
const sourceId = await app.getSourceId( $form, card );
|
||||
|
||||
if ( sourceId === null ) {
|
||||
app.enableSubmitBtn( $form );
|
||||
return;
|
||||
}
|
||||
|
||||
app.submitForm( $form );
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize a Square payments object and retrieve it.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @return {Object|null} Square payments object or null.
|
||||
*/
|
||||
getPaymentsInstance() {
|
||||
if ( ! window.Square ) {
|
||||
app.displaySdkError( $( '.wpforms-square form' ), wpforms_square.i18n.missing_sdk_script );
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
return Square.payments( wpforms_square.client_id, wpforms_square.location_id );
|
||||
} catch ( e ) {
|
||||
const message = ( typeof e === 'object' && Object.prototype.hasOwnProperty.call( e, 'message' ) ) ? e.message : wpforms_square.i18n.missing_creds;
|
||||
|
||||
app.displaySdkError( $( '.wpforms-square form' ), message );
|
||||
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Try to retrieve a Square Card object.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {jQuery} $form Form element.
|
||||
*
|
||||
* @return {Object|null} Square Card object or null.
|
||||
*/
|
||||
async getCardInstance( $form ) {
|
||||
// Applying the modern styles to the card config if needed.
|
||||
// eslint-disable-next-line prefer-const
|
||||
let cardConfig = {};
|
||||
|
||||
cardConfig.style = wpforms_square.card_config.style ? wpforms_square.card_config.style : app.getModernMarkupCardStyles( $form );
|
||||
|
||||
try {
|
||||
const card = await app.payments.card( cardConfig );
|
||||
|
||||
// Attach the Card form to the page.
|
||||
await card.attach( $form.find( '.wpforms-field-square-cardnumber' ).get( 0 ) );
|
||||
|
||||
const eventsList = [ 'focusClassAdded', 'focusClassRemoved' ];
|
||||
const eventsLength = eventsList.length;
|
||||
let counter = 0;
|
||||
|
||||
// Bind the Card events.
|
||||
for ( ; counter < eventsLength; counter++ ) {
|
||||
card.addEventListener( eventsList[ counter ], function( e ) {
|
||||
// Card field is filled.
|
||||
cardData[ e.detail.field ].empty = e.detail.currentState.isEmpty;
|
||||
cardData[ e.detail.field ].valid = e.detail.currentState.isCompletelyValid;
|
||||
|
||||
if ( cardData[ e.detail.field ].valid ) {
|
||||
app.removeFieldError( $form );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
$form.find( '.wpforms-square-credit-card-hidden-input' ).data( 'square-card', card );
|
||||
|
||||
$form.removeClass( 'wpforms-square-initialization' );
|
||||
$form.addClass( 'wpforms-square-initialized' );
|
||||
|
||||
return card;
|
||||
} catch ( e ) {
|
||||
app.displaySdkError( $form, wpforms_square.i18n.card_init_error );
|
||||
|
||||
$form.removeClass( 'wpforms-square-initialization' );
|
||||
|
||||
console.log( 'Error:', e ); // eslint-disable-line no-console
|
||||
console.log( 'Config', cardConfig ); // eslint-disable-line no-console
|
||||
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve a source ID (card nonce).
|
||||
*
|
||||
* @param {jQuery} $form Form element.
|
||||
* @param {Object} card Square Card object.
|
||||
*
|
||||
* @return {string|null} The source ID or null.
|
||||
*/
|
||||
async getSourceId( $form, card ) {
|
||||
try {
|
||||
const response = await card.tokenize( app.getChargeVerifyBuyerDetails( $form ) );
|
||||
|
||||
$form.find( '.wpforms-square-payment-source-id' ).remove();
|
||||
|
||||
if ( response.status !== 'OK' || ! response.token ) {
|
||||
app.displayFormError( app.getCreditCardInput( $form ), app.getResponseError( response ) );
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$form.append( '<input type="hidden" name="wpforms[square][source_id]" class="wpforms-square-payment-source-id" value="' + app.escapeTextString( response.token ) + '">' );
|
||||
|
||||
return response.token;
|
||||
} catch ( e ) {
|
||||
app.displayFormError( app.getCreditCardInput( $form ), wpforms_square.i18n.token_process_fail );
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve a response error message.
|
||||
*
|
||||
* @param {Object} response The response received from a tokenization call.
|
||||
*
|
||||
* @return {string} The response error message.
|
||||
*/
|
||||
getResponseError( response ) {
|
||||
const hasErrors = response.errors && Array.isArray( response.errors ) && response.errors.length;
|
||||
|
||||
return hasErrors ? response.errors[ 0 ].message : wpforms_square.i18n.token_status_error + ' ' + response.status;
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve details about the buyer for a charge.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {jQuery} $form Form element.
|
||||
*
|
||||
* @return {Object} Buyer details.
|
||||
*/
|
||||
getChargeVerifyBuyerDetails( $form ) {
|
||||
return {
|
||||
amount: app.getTotalInMinorUnits( wpforms.amountTotalCalc( $form ) ),
|
||||
billingContact: app.getBillingContactDetails( $form ),
|
||||
currencyCode: wpforms_settings.currency_code,
|
||||
intent: 'CHARGE',
|
||||
customerInitiated: true,
|
||||
sellerKeyedIn: false,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve the total amount in minor units.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {number} total Total amount.
|
||||
*
|
||||
* @return {string} Total amount in minor units.
|
||||
*/
|
||||
getTotalInMinorUnits( total ) {
|
||||
return parseInt( wpforms.numberFormat( total, wpforms_settings.currency_decimal, '', '' ), 10 ).toString();
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve billing contact details.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {jQuery} $form Form element.
|
||||
*
|
||||
* @return {Object} Billing contact details.
|
||||
*/
|
||||
getBillingContactDetails( $form ) { // eslint-disable-line complexity
|
||||
// Get the form ID and billing mapping for this form, if available.
|
||||
const formId = $form.data( 'formid' );
|
||||
const mapping = ( wpforms_square.billing_details && wpforms_square.billing_details[ formId ] ) || {};
|
||||
const result = {};
|
||||
|
||||
// Use mapped selectors if provided.
|
||||
const $emailField = mapping.buyer_email ? $( `.wpforms-field-email[data-field-id="${ mapping.buyer_email }"]` ) : '';
|
||||
const $nameField = mapping.billing_name ? $( `.wpforms-field-name[data-field-id="${ mapping.billing_name }"]` ) : '';
|
||||
const $addressField = mapping.billing_address ? $( `.wpforms-field-address[data-field-id="${ mapping.billing_address }"]` ) : '';
|
||||
|
||||
if ( $emailField.length ) {
|
||||
const emailValue = $emailField.find( 'input' ).first().val(); // Use the first input field knowing there could be confirmation email input as well.
|
||||
if ( emailValue && emailValue.trim() !== '' ) {
|
||||
result.email = emailValue;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $nameField.length ) {
|
||||
jQuery.extend( result, app.getBillingNameDetails( $nameField ) );
|
||||
}
|
||||
|
||||
if ( $addressField.length ) {
|
||||
jQuery.extend( result, app.getBillingAddressDetails( $addressField ) );
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve billing name details.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {jQuery} $field Field element.
|
||||
*
|
||||
* @return {Object} Billing name details.
|
||||
*/
|
||||
getBillingNameDetails( $field ) { // eslint-disable-line complexity
|
||||
const result = {};
|
||||
|
||||
let givenName = '';
|
||||
let familyName = '';
|
||||
|
||||
// Try to find separate first and last name fields.
|
||||
const $firstNameField = $field.find( '.wpforms-field-name-first' );
|
||||
const $lastNameField = $field.find( '.wpforms-field-name-last' );
|
||||
|
||||
if ( $firstNameField.length && $lastNameField.length ) {
|
||||
// Use separate fields if both are present.
|
||||
givenName = $firstNameField.val() || '';
|
||||
familyName = $lastNameField.val() || '';
|
||||
|
||||
if ( givenName && givenName.trim() !== '' ) {
|
||||
result.givenName = givenName;
|
||||
}
|
||||
|
||||
if ( familyName && familyName.trim() !== '' ) {
|
||||
result.familyName = familyName;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Otherwise, fall back to a single name input field.
|
||||
const $nameField = $field.find( 'input' );
|
||||
|
||||
if ( ! $nameField.length ) {
|
||||
return result;
|
||||
}
|
||||
const fullName = $nameField.val().trim();
|
||||
|
||||
if ( ! fullName.length ) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Split full name by space; the first part is givenName,
|
||||
// the rest (if any) are combined as familyName.
|
||||
const nameParts = fullName.split( ' ' );
|
||||
givenName = nameParts.shift() || '';
|
||||
familyName = nameParts.join( ' ' ) || '';
|
||||
|
||||
if ( givenName && givenName.trim() !== '' ) {
|
||||
result.givenName = givenName;
|
||||
}
|
||||
|
||||
if ( familyName && familyName.trim() !== '' ) {
|
||||
result.familyName = familyName;
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve billing address details.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {jQuery} $addressField Field element.
|
||||
*
|
||||
* @return {Object} Billing address details.
|
||||
*/
|
||||
getBillingAddressDetails( $addressField ) { // eslint-disable-line complexity
|
||||
const result = {};
|
||||
|
||||
// For address fields, use the closest wrapper.
|
||||
const $addressWrapper = $addressField.closest( '.wpforms-field' );
|
||||
|
||||
// Retrieve address components, defaulting to empty strings if not found.
|
||||
const addressLine1 = $addressWrapper.find( '.wpforms-field-address-address1' ).val() || '';
|
||||
const addressLine2 = $addressWrapper.find( '.wpforms-field-address-address2' ).val() || '';
|
||||
const city = $addressWrapper.find( '.wpforms-field-address-city' ).val() || '';
|
||||
const state = $addressWrapper.find( '.wpforms-field-address-state' ).val() || '';
|
||||
const country = $addressWrapper.find( '.wpforms-field-address-country' ).val() || 'US';
|
||||
const addressLines = [ addressLine1, addressLine2 ].filter( ( line ) => line && line.trim() !== '' );
|
||||
|
||||
if ( addressLines.length ) {
|
||||
result.addressLines = addressLines;
|
||||
}
|
||||
|
||||
if ( city && city.trim() !== '' ) {
|
||||
result.city = city;
|
||||
}
|
||||
if ( state && state.trim() !== '' ) {
|
||||
result.state = state;
|
||||
}
|
||||
if ( country && country.trim() !== '' ) {
|
||||
result.countryCode = country;
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve a jQuery selector for Credit Card hidden input.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {jQuery} $form Form element.
|
||||
*
|
||||
* @return {jQuery} Credit Card hidden input.
|
||||
*/
|
||||
getCreditCardInput( $form ) {
|
||||
return $form.find( '.wpforms-square-credit-card-hidden-input' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Submit the form using the original submitHandler.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {jQuery} $form Form element.
|
||||
*/
|
||||
submitForm( $form ) {
|
||||
const validator = $form.data( 'validator' );
|
||||
|
||||
if ( validator ) {
|
||||
originalSubmitHandler( $form );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine if a credit card should be processed.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {jQuery} $form Form element.
|
||||
*
|
||||
* @return {boolean} True if a credit card should be processed.
|
||||
*/
|
||||
isProcessedCard( $form ) {
|
||||
const $squareDiv = $form.find( '.wpforms-field-square-cardnumber' );
|
||||
const condHidden = $squareDiv.closest( '.wpforms-field-square' ).hasClass( 'wpforms-conditional-hide' );
|
||||
const ccRequired = !! $squareDiv.data( 'required' );
|
||||
|
||||
if ( condHidden ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ccRequired || app.isCardDataNotEmpty();
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine if card data not empty.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @return {boolean} True if at least one credit card sub-field is filled.
|
||||
*/
|
||||
isCardDataNotEmpty() {
|
||||
return ! cardData.cardNumber.empty || ! cardData.expirationDate.empty || ! cardData.cvv.empty || ! cardData.postalCode.empty;
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine if card data is completely valid.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @return {boolean} True if at least one credit card sub-field is filled.
|
||||
*/
|
||||
isCardDataValid() {
|
||||
return cardData.cardNumber.valid && cardData.expirationDate.valid && cardData.cvv.valid && cardData.postalCode.valid;
|
||||
},
|
||||
|
||||
/**
|
||||
* Display a SDK error.
|
||||
*
|
||||
* @param {jQuery} $form Form element.
|
||||
* @param {string} message Error messages.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
displaySdkError( $form, message ) {
|
||||
$form
|
||||
.find( '.wpforms-square-credit-card-hidden-input' )
|
||||
.closest( '.wpforms-field-square-number' )
|
||||
.append( $( '<label></label>', {
|
||||
text: message,
|
||||
class: 'wpforms-error',
|
||||
} ) );
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove field error.
|
||||
*
|
||||
* @param {jQuery} $form Form element.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
removeFieldError( $form ) {
|
||||
$form.find( '.wpforms-field-square-number .wpforms-error' ).remove();
|
||||
},
|
||||
|
||||
/**
|
||||
* Display a field error using jQuery Validate library.
|
||||
*
|
||||
* @param {jQuery} $field Form element.
|
||||
* @param {string} message Error messages.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
displayFormError( $field, message ) {
|
||||
const fieldName = $field.attr( 'name' );
|
||||
const $form = $field.closest( 'form' );
|
||||
const error = {};
|
||||
|
||||
error[ fieldName ] = message;
|
||||
|
||||
wpforms.displayFormAjaxFieldErrors( $form, error );
|
||||
wpforms.scrollToError( $field );
|
||||
},
|
||||
|
||||
/**
|
||||
* Disable submit button for the form.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {jQuery} $form Form element.
|
||||
*/
|
||||
disableSubmitBtn( $form ) {
|
||||
$form.find( '.wpforms-submit' ).prop( 'disabled', true );
|
||||
},
|
||||
|
||||
/**
|
||||
* Enable submit button for the form.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {jQuery} $form Form element.
|
||||
*/
|
||||
enableSubmitBtn( $form ) {
|
||||
$form.find( '.wpforms-submit' ).prop( 'disabled', false );
|
||||
},
|
||||
|
||||
/**
|
||||
* Replaces &, <, >, ", `, and ' with their escaped counterparts.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {string} string String to escape.
|
||||
*
|
||||
* @return {string} Escaped string.
|
||||
*/
|
||||
escapeTextString( string ) {
|
||||
return $( '<span></span>' ).text( string ).html();
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback for a page changing.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {Event} event Event.
|
||||
* @param {number} currentPage Current page.
|
||||
* @param {jQuery} $form Current form.
|
||||
* @param {string} action The navigation action.
|
||||
*/
|
||||
pageChange( event, currentPage, $form, action ) { // eslint-disable-line complexity
|
||||
const $squareDiv = $form.find( '.wpforms-field-square-cardnumber' );
|
||||
|
||||
// Stop navigation through page break pages.
|
||||
if (
|
||||
! $squareDiv.is( ':visible' ) ||
|
||||
( ! $squareDiv.data( 'required' ) && ! app.isCardDataNotEmpty() ) ||
|
||||
( app.lockedPageToSwitch && app.lockedPageToSwitch !== currentPage ) ||
|
||||
action === 'prev'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( app.isCardDataValid() ) {
|
||||
app.removeFieldError( $form );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
app.lockedPageToSwitch = currentPage;
|
||||
|
||||
app.displayFormError( app.getCreditCardInput( $form ), wpforms_square.i18n.empty_details );
|
||||
event.preventDefault();
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback for a after page changing.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
afterPageChange() {
|
||||
window.dispatchEvent( new Event( 'resize' ) );
|
||||
},
|
||||
|
||||
/**
|
||||
* Get CSS property value.
|
||||
* In case of exception return empty string.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {jQuery} $element Element.
|
||||
* @param {string} property Property.
|
||||
*
|
||||
* @return {string} Property value.
|
||||
*/
|
||||
getCssPropertyValue( $element, property ) {
|
||||
try {
|
||||
return $element.css( property );
|
||||
} catch ( e ) {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine whether modern style are needed.
|
||||
*
|
||||
* Force run on the classic markup if it is conversational or lead form.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @return {boolean} True if the form needs styles.
|
||||
*/
|
||||
needsStyles() {
|
||||
// Styles are not needed if the classic markup is used
|
||||
// and it's neither conversational nor lead form.
|
||||
if (
|
||||
( ! window.WPForms || ! WPForms.FrontendModern ) &&
|
||||
! $( '#wpforms-conversational-form-page' ).length &&
|
||||
! $( '.wpforms-lead-forms-container' ).length
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get modern card styles.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {jQuery} $form Current form.
|
||||
*
|
||||
* @return {Object} Card styles object.
|
||||
*/
|
||||
getModernMarkupCardStyles( $form ) {
|
||||
if ( ! app.needsStyles() ) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const $hiddenInput = app.getCreditCardInput( $form ),
|
||||
hiddenInputColor = app.getCssPropertyValue( $hiddenInput, 'color' ),
|
||||
inputStyle = {
|
||||
fontSize: app.getCssPropertyValue( $hiddenInput, 'font-size' ),
|
||||
colorText: hiddenInputColor,
|
||||
colorTextPlaceholder: hiddenInputColor,
|
||||
};
|
||||
|
||||
// Check if WPFormsUtils.cssColorsUtils object is available.
|
||||
if ( WPFormsUtils.hasOwnProperty( 'cssColorsUtils' ) &&
|
||||
typeof WPFormsUtils.cssColorsUtils.getColorWithOpacity === 'function' ) {
|
||||
inputStyle.colorText = WPFormsUtils.cssColorsUtils.getColorWithOpacity( hiddenInputColor );
|
||||
inputStyle.colorTextPlaceholder = WPFormsUtils.cssColorsUtils.getColorWithOpacity( hiddenInputColor, '0.5' );
|
||||
}
|
||||
|
||||
return {
|
||||
input: {
|
||||
color: inputStyle.colorText,
|
||||
fontSize: inputStyle.fontSize,
|
||||
},
|
||||
'input::placeholder': {
|
||||
color: inputStyle.colorTextPlaceholder,
|
||||
},
|
||||
'input.is-error': {
|
||||
color: inputStyle.colorText,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
// Provide access to public functions/properties.
|
||||
return app;
|
||||
}( document, window, jQuery ) );
|
||||
|
||||
// Initialize.
|
||||
WPFormsSquare.init();
|
||||
Vendored
Executable
+1
File diff suppressed because one or more lines are too long
Executable
+669
@@ -0,0 +1,669 @@
|
||||
/* global wpforms_builder, wpforms_builder_stripe, WPFormsBuilderPaymentsUtils */
|
||||
|
||||
// noinspection ES6ConvertVarToLetConst
|
||||
/**
|
||||
* Stripe builder function.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*/
|
||||
// eslint-disable-next-line no-var
|
||||
var WPFormsStripeModernBuilder = window.WPFormsStripeModernBuilder || ( function( document, window, $ ) {
|
||||
/**
|
||||
* Elements holder.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
let el = {};
|
||||
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const app = {
|
||||
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*/
|
||||
init() {
|
||||
$( app.ready );
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialized once the DOM is fully loaded.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*/
|
||||
ready() {
|
||||
app.customMetadataActions();
|
||||
|
||||
if ( app.isLegacySettings() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Cache DOM elements.
|
||||
el = {
|
||||
$alert: $( '#wpforms-stripe-credit-card-alert' ),
|
||||
$panelContent: $( '#wpforms-panel-content-section-payment-stripe' ),
|
||||
$feeNotice: $( '.wpforms-stripe-notice-info' ),
|
||||
};
|
||||
|
||||
app.bindUIActions();
|
||||
app.bindPlanUIActions();
|
||||
|
||||
if ( ! wpforms_builder_stripe.is_pro ) {
|
||||
const baseSelector = '.wpforms-panel-content-section-stripe',
|
||||
toggleInput = `${ baseSelector } .wpforms-panel-content-section-payment-toggle input`,
|
||||
planNameInput = `${ baseSelector } .wpforms-panel-content-section-payment-plan-name input`;
|
||||
|
||||
$( toggleInput ).each( WPFormsBuilderPaymentsUtils.toggleContent );
|
||||
$( planNameInput ).each( WPFormsBuilderPaymentsUtils.checkPlanName );
|
||||
|
||||
$( '#wpforms-panel-payments' )
|
||||
.on( 'click', toggleInput, WPFormsBuilderPaymentsUtils.toggleContent )
|
||||
.on( 'click', `${ baseSelector } .wpforms-panel-content-section-payment-plan-head-buttons-toggle`, WPFormsBuilderPaymentsUtils.togglePlan )
|
||||
.on( 'click', `${ baseSelector } .wpforms-panel-content-section-payment-plan-head-buttons-delete`, WPFormsBuilderPaymentsUtils.deletePlan )
|
||||
.on( 'input', planNameInput, WPFormsBuilderPaymentsUtils.renamePlan )
|
||||
.on( 'focusout', planNameInput, WPFormsBuilderPaymentsUtils.checkPlanName );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Process custom metadata actions.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*/
|
||||
customMetadataActions() {
|
||||
$( '#wpforms-panel-payments' )
|
||||
.on( 'focusout', '.wpforms-panel-field-stripe-custom-metadata-meta-key', function() {
|
||||
// Remove invalid characters for meta key.
|
||||
$( this ).val( $( this ).val().replace( /[^\p{L}\p{N}_-]/gu, '' ) );
|
||||
} )
|
||||
// Add metadata row.
|
||||
.on( 'click', '.wpforms-panel-content-section-stripe-custom-metadata-add', function( e ) {
|
||||
e.preventDefault();
|
||||
|
||||
const $table = $( this ).parents( '.wpforms-panel-content-section-stripe-custom-metadata-table' ),
|
||||
$lastRow = $table.find( 'tr' ).last(),
|
||||
$clone = $lastRow.clone( true ),
|
||||
lastID = $lastRow.data( 'key' ),
|
||||
nextID = lastID + 1;
|
||||
|
||||
// Update row key ID.
|
||||
$clone.attr( 'data-key', nextID );
|
||||
|
||||
// Increment the counter.
|
||||
$clone.html( $clone.html().replaceAll( '[' + lastID + ']', '[' + nextID + ']' ).replaceAll( '-' + lastID + '-', '-' + nextID + '-' ) );
|
||||
|
||||
// Clear values.
|
||||
$clone.find( 'select, input' ).val( '' );
|
||||
|
||||
// Re-enable "delete" button.
|
||||
$clone.find( '.wpforms-panel-content-section-stripe-custom-metadata-delete' ).removeClass( 'hidden' );
|
||||
|
||||
// Remove error classes.
|
||||
$clone.find( '.wpforms-required-field-error' ).removeClass( 'wpforms-required-field-error' );
|
||||
|
||||
// Put it back to the table.
|
||||
$table.find( 'tbody' ).append( $clone.get( 0 ) );
|
||||
} )
|
||||
// Delete metadata row.
|
||||
.on( 'click', '.wpforms-panel-content-section-stripe-custom-metadata-delete', function( e ) {
|
||||
e.preventDefault();
|
||||
|
||||
$( this ).parents( '.wpforms-panel-content-section-stripe-custom-metadata-table tr' ).remove();
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Process various events as a response to UI interactions.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*/
|
||||
bindUIActions() {
|
||||
const $builder = $( '#wpforms-builder' );
|
||||
|
||||
$builder.on( 'wpformsFieldDelete', app.disableNotifications )
|
||||
.on( 'wpformsSaved', app.requiredFieldsCheck )
|
||||
.on( 'wpformsFieldAdd', app.fieldAdded )
|
||||
.on( 'wpformsFieldDelete', app.fieldDeleted )
|
||||
.on( 'wpformsPaymentsPlanCreated', app.toggleMultiplePlansWarning )
|
||||
.on( 'wpformsPaymentsPlanCreated', app.bindPlanUIActions )
|
||||
.on( 'wpformsPaymentsPlanDeleted', app.toggleMultiplePlansWarning );
|
||||
|
||||
el.$panelContent
|
||||
.find( '.wpforms-panel-content-section-payment-one-time' )
|
||||
.on( 'change', '.wpforms-panel-field-stripe-custom-metadata-meta-key', app.resetCustomMetaKeyErrorClass );
|
||||
},
|
||||
|
||||
/**
|
||||
* Bind plan UI actions.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
bindPlanUIActions() {
|
||||
el.$panelContent.find( '.wpforms-panel-content-section-payment-plan-body .wpforms-panel-field-select select[name*="email"]' ).on( 'change', app.resetEmailAlertErrorClass );
|
||||
el.$panelContent.find( '.wpforms-panel-content-section-payment-plan-body .wpforms-panel-field-stripe-custom-metadata-meta-key' ).on( 'change', app.resetCustomMetaKeyErrorClass );
|
||||
el.$panelContent.find( '.wpforms-panel-content-section-payment-plan-period select' ).on( 'change', app.resetCyclesValues );
|
||||
},
|
||||
|
||||
/**
|
||||
* On form save notify users about required fields.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*/
|
||||
requiredFieldsCheck() {
|
||||
const validationState = app.validateFields();
|
||||
|
||||
if ( ! validationState?.invalid ) {
|
||||
return;
|
||||
}
|
||||
|
||||
app.openAlert(
|
||||
app.getAlertMessage( validationState.type ),
|
||||
() => validationState.invalid.$element.focus()
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Validates form fields based on their visibility and specific conditions.
|
||||
*
|
||||
* @since 1.9.8.3
|
||||
*
|
||||
* @return {Object|boolean} Returns an object representing the validation state or `true` if the panel content is hidden.
|
||||
*/
|
||||
validateFields() {
|
||||
if ( el.$panelContent.hasClass( 'wpforms-hidden' ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// We want to determine which fields are not filled to display an appropriate error message.
|
||||
// Also, it contains the type of the payment: one-time or recurring.
|
||||
const validationState = {};
|
||||
|
||||
if ( $( '#wpforms-panel-field-stripe-enable_one_time' ).is( ':checked' ) ) {
|
||||
app.validateOneTimeFields( validationState );
|
||||
}
|
||||
|
||||
if ( $( '#wpforms-panel-field-stripe-enable_recurring' ).is( ':checked' ) ) {
|
||||
app.validateRecurringFields( validationState );
|
||||
}
|
||||
|
||||
return validationState;
|
||||
},
|
||||
|
||||
/**
|
||||
* Validates the one-time payment fields within the form's panel content.
|
||||
*
|
||||
* @since 1.9.8.3
|
||||
*
|
||||
* @param {Object} validationState The current validation state object.
|
||||
*/
|
||||
validateOneTimeFields( validationState ) {
|
||||
const $oneTimeScope = el.$panelContent.find( '.wpforms-panel-content-section-payment-one-time' );
|
||||
|
||||
validationState.type = 'one-time';
|
||||
app.validateCustomMetaTable( $oneTimeScope, validationState );
|
||||
},
|
||||
|
||||
/**
|
||||
* Validates recurring fields within the payment plan section of the panel content.
|
||||
*
|
||||
* @since 1.9.8.3
|
||||
*
|
||||
* @param {Object} validationState An object representing the current state of validation.
|
||||
*/
|
||||
validateRecurringFields( validationState ) {
|
||||
validationState.type = validationState.type ? 'both' : 'recurring';
|
||||
|
||||
el.$panelContent.find( '.wpforms-panel-content-section-payment-plan' ).each( function() {
|
||||
const $plan = $( this );
|
||||
|
||||
app.validateEmailField( $plan, validationState );
|
||||
app.validateCustomMetaTable( $plan, validationState );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Validates the email field for a given plan and updates the validation state if the field is empty.
|
||||
*
|
||||
* @since 1.9.8.3
|
||||
*
|
||||
* @param {Object} $plan jQuery object representing the plan element.
|
||||
* @param {Object} validationState Object representing the validation state that will be updated.
|
||||
*/
|
||||
validateEmailField( $plan, validationState ) {
|
||||
const planId = $plan.data( 'plan-id' );
|
||||
const $emailField = $( `#wpforms-panel-field-stripe-recurring-${ planId }-email` );
|
||||
|
||||
if ( $emailField.val() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$emailField.addClass( 'wpforms-required-field-error' );
|
||||
validationState.invalid = validationState.invalid ?? {};
|
||||
validationState.invalid.email = true;
|
||||
|
||||
if ( ! validationState.invalid.$element ) {
|
||||
validationState.invalid.$element = $emailField;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Validates the custom metadata table rows within the specified scope.
|
||||
*
|
||||
* @since 1.9.8.3
|
||||
*
|
||||
* @param {Object} $scope The jQuery object representing the current scope containing the custom metadata table.
|
||||
* @param {Object} validationState The object used to store the validation state for the custom metadata rows.
|
||||
*
|
||||
* @return {boolean|void} Returns `true` if no rows are present for validation, otherwise no return value.
|
||||
*/
|
||||
validateCustomMetaTable( $scope, validationState ) {
|
||||
const $customMetaRows = $scope.find( '.wpforms-panel-content-section-stripe-custom-metadata-table tr[data-key]' );
|
||||
|
||||
if ( ! $customMetaRows.length ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$customMetaRows.each( function() {
|
||||
const $row = $( this );
|
||||
|
||||
if ( app.isValidCustomMetaRow( $row ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
validationState.invalid = validationState.invalid ?? {};
|
||||
validationState.invalid.customMeta = true;
|
||||
|
||||
if ( ! validationState.invalid.$element ) {
|
||||
validationState.invalid.$element = $row.find( '.wpforms-panel-field-stripe-custom-metadata-meta-key' );
|
||||
}
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Validates if a custom metadata row in the form is properly filled.
|
||||
*
|
||||
* @since 1.9.8.3
|
||||
*
|
||||
* @param {Object} $row jQuery object representing the custom metadata row to validate.
|
||||
*
|
||||
* @return {boolean} True if the custom metadata row is valid, otherwise false.
|
||||
*/
|
||||
isValidCustomMetaRow( $row ) {
|
||||
const $metaKey = $row.find( '.wpforms-panel-field-stripe-custom-metadata-meta-key' );
|
||||
const isValid = (
|
||||
! $row.find( '.wpforms-panel-field-stripe-custom-metadata-object-type' ).val() ||
|
||||
! $row.find( '.wpforms-panel-field-stripe-custom-metadata-meta-value' ).val() ||
|
||||
$metaKey.val()
|
||||
);
|
||||
|
||||
$metaKey.toggleClass( 'wpforms-required-field-error', ! isValid );
|
||||
|
||||
return isValid;
|
||||
},
|
||||
|
||||
/**
|
||||
* Maybe reset required email field error class.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
resetEmailAlertErrorClass() {
|
||||
$( this ).toggleClass( 'wpforms-required-field-error', ! $( this ).val() );
|
||||
},
|
||||
|
||||
/**
|
||||
* Resets the error class for a custom meta key row.
|
||||
*
|
||||
* @since 1.9.8.3
|
||||
*/
|
||||
resetCustomMetaKeyErrorClass() {
|
||||
const $row = $( this ).closest( 'tr[data-key]' );
|
||||
|
||||
app.isValidCustomMetaRow( $row );
|
||||
},
|
||||
|
||||
/**
|
||||
* Show alert for required recurring email field.
|
||||
*
|
||||
* @since 1.8.4
|
||||
* @deprecated 1.9.8.3
|
||||
*/
|
||||
recurringEmailAlert() {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn( 'WARNING! Function "WPFormsStripeModernBuilder.recurringEmailAlert()" has been deprecated, please use the "WPFormsStripeModernBuilder.openAlert()" function instead!' );
|
||||
|
||||
app.openAlert( wpforms_builder.stripe_recurring_email );
|
||||
},
|
||||
|
||||
/**
|
||||
* Opens an alert dialog with a customized message and optional callback action.
|
||||
*
|
||||
* @since 1.9.8.3
|
||||
*
|
||||
* @param {string} alertMessage The message to be displayed in the alert dialog.
|
||||
* @param {Function} onClose Optional callback function to execute when the alert is closed.
|
||||
*/
|
||||
openAlert( alertMessage, onClose = () => {} ) {
|
||||
$.alert( {
|
||||
title: wpforms_builder.stripe_recurring_heading,
|
||||
content: alertMessage,
|
||||
icon: 'fa fa-exclamation-circle',
|
||||
type: 'red',
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: wpforms_builder.ok,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
action: onClose,
|
||||
},
|
||||
},
|
||||
onOpen() {
|
||||
$( '.wpforms-stripe-settings-redirect' ).on( 'click', app.settingsRedirect );
|
||||
},
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns an alert message based on the specified payment type and Stripe visibility settings.
|
||||
*
|
||||
* @since 1.9.8.3
|
||||
*
|
||||
* @param {string} paymentType The payment type (e.g., 'one-time', 'recurring', or 'both') used to determine the alert message.
|
||||
*
|
||||
* @return {string} The appropriate alert message for the specified payment type.
|
||||
*/
|
||||
getAlertMessage( paymentType ) {
|
||||
if ( ! $( '.wpforms-panel-content-section-stripe' ).is( ':visible' ) ) {
|
||||
return wpforms_builder.stripe_recurring_settings;
|
||||
}
|
||||
|
||||
const typesMap = {
|
||||
'one-time': 'stripe_required_one_time_fields',
|
||||
recurring: 'stripe_required_recurring_fields',
|
||||
both: 'stripe_required_both_fields',
|
||||
};
|
||||
|
||||
return wpforms_builder[ typesMap[ paymentType ] ?? typesMap.recurring ];
|
||||
},
|
||||
|
||||
/**
|
||||
* Redirect to the settings tab.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
settingsRedirect() {
|
||||
// Open the Stripe settings tab.
|
||||
$( '.wpforms-panel-payments-button' ).trigger( 'click' );
|
||||
$( '.wpforms-panel-sidebar-section-stripe' ).trigger( 'click' );
|
||||
|
||||
// Scroll to the Stripe settings.
|
||||
window.location.href = window.location.pathname + window.location.search + '#wpforms-panel-field-stripe-enable_recurring-wrap';
|
||||
|
||||
// Close the alert.
|
||||
$( this ).closest( '.jconfirm-box' ).find( '.btn-confirm' ).trigger( 'click' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Disable notifications.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*
|
||||
* @param {Object} e Event object.
|
||||
* @param {number} id Field ID.
|
||||
* @param {string} type Field type.
|
||||
*/
|
||||
disableNotifications( e, id, type ) {
|
||||
if ( ! app.isStripeField( type ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( app.hasStripeCreditCardFieldInBuilder() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const $notificationWrap = $( '.wpforms-panel-content-section-notifications [id*="-stripe-wrap"]' );
|
||||
|
||||
$notificationWrap.find( 'input[id*="-stripe"]' ).prop( 'checked', false );
|
||||
$notificationWrap.addClass( 'wpforms-hidden' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine is legacy settings is loaded.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*
|
||||
* @return {boolean} True is legacy settings loaded.
|
||||
*/
|
||||
isLegacySettings() {
|
||||
return $( '#wpforms-panel-field-stripe-enable' ).length;
|
||||
},
|
||||
|
||||
/**
|
||||
* We have to do several actions when the "Stripe" field is added.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*
|
||||
* @param {Object} e Event object.
|
||||
* @param {number} id Field ID.
|
||||
* @param {string} type Field type.
|
||||
*/
|
||||
fieldAdded( e, id, type ) {
|
||||
if ( ! app.isStripeField( type ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! app.hasStripeCreditCardFieldInBuilder() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
app.settingsToggle( true );
|
||||
el.$feeNotice.toggleClass( 'wpforms-hidden' );
|
||||
},
|
||||
|
||||
/**
|
||||
* We have to do several actions when the "Stripe" field is deleted.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*
|
||||
* @param {Object} e Event object.
|
||||
* @param {number} id Field ID.
|
||||
* @param {string} type Field type.
|
||||
*/
|
||||
fieldDeleted( e, id, type ) {
|
||||
if ( ! app.isStripeField( type ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( app.hasStripeCreditCardFieldInBuilder() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
app.settingsToggle( false );
|
||||
app.disablePayments();
|
||||
el.$feeNotice.toggleClass( 'wpforms-hidden' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine if a field type is Stripe credit card.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*
|
||||
* @param {string} type Field type.
|
||||
*
|
||||
* @return {boolean} True if Stripe field.
|
||||
*/
|
||||
isStripeField( type ) {
|
||||
return type === wpforms_builder_stripe.field_slug;
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if the Stripe Credit Card field is in the form builder.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @return {boolean} True if the Stripe Credit Card field is in the builder.
|
||||
*/
|
||||
hasStripeCreditCardFieldInBuilder() {
|
||||
return $( `.wpforms-field.wpforms-field-${ wpforms_builder_stripe.field_slug }` ).length > 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggles visibility of multiple plans warning.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*/
|
||||
toggleMultiplePlansWarning() {
|
||||
el.$panelContent.find( '.wpforms-stripe-multiple-plans-warning' ).toggleClass( 'wpforms-hidden', el.$panelContent.find( '.wpforms-panel-content-section-payment-plan' ).length === 1 );
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggles visibility of the Stripe addon settings.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*
|
||||
* @param {boolean} display Show or hide settings.
|
||||
*/
|
||||
settingsToggle( display ) {
|
||||
if (
|
||||
! el.$alert.length &&
|
||||
! el.$panelContent.length
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
el.$alert.toggleClass( 'wpforms-hidden', display );
|
||||
el.$panelContent.toggleClass( 'wpforms-hidden', ! display );
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggle payments content.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*/
|
||||
toggleContent() {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn( 'WARNING! Function "WPFormsStripeModernBuilder.toggleContent()" has been deprecated, please use the new "WPFormsPaymentsUtils.toggleContent()" function instead!' );
|
||||
|
||||
WPFormsBuilderPaymentsUtils.toggleContent();
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggle a plan content.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*/
|
||||
togglePlan() {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn( 'WARNING! Function "WPFormsStripeModernBuilder.togglePlan()" has been deprecated, please use the new "WPFormsPaymentsUtils.togglePlan()" function instead!' );
|
||||
|
||||
WPFormsBuilderPaymentsUtils.togglePlan();
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete a plan.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*/
|
||||
deletePlan() {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn( 'WARNING! Function "WPFormsStripeModernBuilder.checkPlanName()" has been deprecated, please use the new "WPFormsPaymentsUtils.deletePlan()" function instead!' );
|
||||
|
||||
WPFormsBuilderPaymentsUtils.deletePlan();
|
||||
},
|
||||
|
||||
/**
|
||||
* Check a plan name on empty value.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*/
|
||||
checkPlanName() {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn( 'WARNING! Function "WPFormsStripeModernBuilder.checkPlanName()" has been deprecated, please use the new "WPFormsPaymentsUtils.checkPlanName()" function instead!' );
|
||||
|
||||
WPFormsBuilderPaymentsUtils.checkPlanName();
|
||||
},
|
||||
|
||||
/**
|
||||
* Rename a plan.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*/
|
||||
renamePlan() {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn( 'WARNING! Function "WPFormsStripeModernBuilder.renamePlan()" has been deprecated, please use the new "WPFormsPaymentsUtils.renamePlan()" function instead!' );
|
||||
|
||||
WPFormsBuilderPaymentsUtils.renamePlan();
|
||||
},
|
||||
|
||||
/**
|
||||
* Make sure that "One-Time Payments" and "Recurring Payments" toggles are turned off.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
disablePayments() {
|
||||
const toggleInput = $( '#wpforms-panel-field-stripe-enable_one_time, #wpforms-panel-field-stripe-enable_recurring' );
|
||||
|
||||
toggleInput.prop( 'checked', false ).trigger( 'change' ).each( WPFormsBuilderPaymentsUtils.toggleContent );
|
||||
},
|
||||
|
||||
/**
|
||||
* Reset cycles dropdown values based on the period selection.
|
||||
*
|
||||
* @since 1.9.8
|
||||
*/
|
||||
resetCyclesValues() {
|
||||
const $el = $( this ),
|
||||
$select = $el.closest( '.wpforms-panel-content-section-payment-plan-body' ).find( '.wpforms-panel-content-section-payment-plan-cycles select' ),
|
||||
selectedValue = $select.val(); // Save current selected value
|
||||
|
||||
let max;
|
||||
|
||||
// Determine the maximum number of cycles based on the selected period.
|
||||
// It has intentionally not been passed from PHP to avoid unnecessary complexity or unexpected behavior.
|
||||
switch ( $el.val() ) {
|
||||
case 'yearly':
|
||||
max = 20;
|
||||
break;
|
||||
case 'semiyearly':
|
||||
max = 40;
|
||||
break;
|
||||
case 'quarterly':
|
||||
max = 80;
|
||||
break;
|
||||
|
||||
default:
|
||||
max = wpforms_builder_stripe.cycles_max;
|
||||
}
|
||||
|
||||
const options = [
|
||||
$( '<option>', { value: 'unlimited', text: wpforms_builder_stripe.i18n.cycles_default } ),
|
||||
];
|
||||
|
||||
for ( let i = 1; i <= max; i++ ) {
|
||||
options.push( $( '<option>', { value: i, text: i } ) );
|
||||
}
|
||||
|
||||
// Populate select and re-apply selected value if still valid
|
||||
$select.empty().append( options ).val( selectedValue );
|
||||
|
||||
// If selected value is no longer valid, default to 'unlimited'
|
||||
if ( $select.val() !== selectedValue ) {
|
||||
$select.val( 'unlimited' );
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// Provide access to public functions/properties.
|
||||
return app;
|
||||
}( document, window, jQuery ) );
|
||||
|
||||
// Initialize.
|
||||
WPFormsStripeModernBuilder.init();
|
||||
Vendored
Executable
+1
File diff suppressed because one or more lines are too long
Executable
+222
@@ -0,0 +1,222 @@
|
||||
/* global wpforms_builder, wpforms_builder_stripe */
|
||||
|
||||
/**
|
||||
* WPForms Stripe Card Field function.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var WPFormsStripeCardField = window.WPFormsStripeCardField || ( function( document, window, $ ) {
|
||||
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*
|
||||
* @type {object}
|
||||
*/
|
||||
const app = {
|
||||
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*/
|
||||
init: function() {
|
||||
|
||||
app.bindUIActions();
|
||||
},
|
||||
|
||||
/**
|
||||
* Process various events as a response to UI interactions.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*/
|
||||
bindUIActions: function() {
|
||||
|
||||
$( document ).on( 'wpformsSaved', app.ajaxRequiredCheck );
|
||||
$( document ).on( 'wpformsSaved', app.paymentsEnabledCheck );
|
||||
|
||||
$( document ).on( 'click', '#wpforms-add-fields-' + wpforms_builder_stripe.field_slug, app.stripeKeysCheck );
|
||||
$( document ).on( 'change', '.wpforms-field-option-stripe-credit-card .wpforms-field-option-row-sublabel_position select', app.sublabelPositionChange );
|
||||
$( document ).on( 'change', '.wpforms-field-option-stripe-credit-card .wpforms-field-option-row-link_email select', app.linkEmailChange );
|
||||
|
||||
$( document ).on( 'wpformsFieldAdd', app.disableAddCardButton );
|
||||
$( document ).on( 'wpformsFieldDelete', app.enableAddCardButton );
|
||||
$( document ).on( 'wpformsFieldDelete', app.maybeResetLinkEmailField );
|
||||
},
|
||||
|
||||
/**
|
||||
* On form save notify users if AJAX submission is required.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*/
|
||||
ajaxRequiredCheck: function() {
|
||||
|
||||
if ( ! $( '.wpforms-field.wpforms-field-' + wpforms_builder_stripe.field_slug ).length ||
|
||||
$( '#wpforms-panel-field-settings-ajax_submit' ).is( ':checked' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.alert( {
|
||||
title: wpforms_builder.heads_up,
|
||||
content: wpforms_builder.stripe_ajax_required,
|
||||
icon: 'fa fa-exclamation-circle',
|
||||
type: 'orange',
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: wpforms_builder.ok,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
},
|
||||
},
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* On form save notify users if Stripe payments are not enabled.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*/
|
||||
paymentsEnabledCheck: function() {
|
||||
|
||||
if ( ! $( `.wpforms-field.wpforms-field-${ wpforms_builder_stripe.field_slug }:visible` ).length ||
|
||||
$( '#wpforms-panel-field-stripe-enable' ).is( ':checked' ) ||
|
||||
$( '#wpforms-panel-field-stripe-enable_one_time' ).is( ':checked' ) ||
|
||||
$( '#wpforms-panel-field-stripe-enable_recurring' ).is( ':checked' )
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.alert( {
|
||||
title: wpforms_builder.heads_up,
|
||||
content: wpforms_builder.payments_enabled_required,
|
||||
icon: 'fa fa-exclamation-circle',
|
||||
type: 'red',
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: wpforms_builder.ok,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
},
|
||||
},
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* On adding Stripe Credit Card field notify users if Stripe keys are missing.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*/
|
||||
stripeKeysCheck: function() {
|
||||
|
||||
if ( ! $( this ).hasClass( 'stripe-keys-required' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.alert( {
|
||||
title: wpforms_builder.heads_up,
|
||||
content: wpforms_builder.stripe_keys_required,
|
||||
icon: 'fa fa-exclamation-circle',
|
||||
type: 'orange',
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: wpforms_builder.ok,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
},
|
||||
},
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Disable "Add Card" button in the fields list.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*
|
||||
* @param {object} e Event object.
|
||||
* @param {number} id Field ID.
|
||||
* @param {string} type Field type.
|
||||
*/
|
||||
disableAddCardButton: function( e, id, type ) {
|
||||
|
||||
if ( wpforms_builder_stripe.field_slug === type ) {
|
||||
$( '#wpforms-add-fields-' + wpforms_builder_stripe.field_slug )
|
||||
.prop( 'disabled', true );
|
||||
|
||||
app.paymentsEnabledCheck();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Enable "Add Card" button in the fields list.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*
|
||||
* @param {object} e Event object.
|
||||
* @param {number} id Field ID.
|
||||
* @param {string} type Field type.
|
||||
*/
|
||||
enableAddCardButton: function( e, id, type ) {
|
||||
|
||||
if ( wpforms_builder_stripe.field_slug === type ) {
|
||||
$( '#wpforms-add-fields-' + wpforms_builder_stripe.field_slug )
|
||||
.prop( 'disabled', false );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Switch sublabels preview mode.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*/
|
||||
sublabelPositionChange: function() {
|
||||
|
||||
const fieldId = $( this ).parent().data( 'field-id' ),
|
||||
$fieldPreview = $( `#wpforms-field-${fieldId}` ).find( '.wpforms-stripe-payment-element' );
|
||||
|
||||
$fieldPreview.toggleClass( 'above' );
|
||||
$fieldPreview.toggleClass( 'floating' );
|
||||
$fieldPreview.find( 'select' ).val( $fieldPreview.hasClass( 'above' ) ? 'empty' : 'country' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Switch Link Email Field mapping.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*/
|
||||
linkEmailChange: function() {
|
||||
|
||||
const fieldId = $( this ).parent().data( 'field-id' );
|
||||
|
||||
$( `#wpforms-field-${fieldId}` ).find( '.wpforms-stripe-link-email' ).toggleClass( 'wpforms-hidden', $( this ).val() !== '' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Maybe reset link email field if mapped email was removed.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*
|
||||
* @param {object} e Event object.
|
||||
* @param {number} id Field ID.
|
||||
* @param {string} type Field type.
|
||||
*/
|
||||
maybeResetLinkEmailField: function( e, id, type ) {
|
||||
|
||||
if ( type !== 'email' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$( '.wpforms-field-option-stripe-credit-card .wpforms-field-option-row-link_email select' ).trigger( 'change' );
|
||||
},
|
||||
};
|
||||
|
||||
// Provide access to public functions/properties.
|
||||
return app;
|
||||
|
||||
}( document, window, jQuery ) );
|
||||
|
||||
// Initialize.
|
||||
WPFormsStripeCardField.init();
|
||||
wp-content/plugins/wpforms-lite/assets/js/integrations/stripe/admin-builder-stripe-card-field.min.js
Vendored
Executable
+1
@@ -0,0 +1 @@
|
||||
var WPFormsStripeCardField=window.WPFormsStripeCardField||((e,s)=>{let t={init:function(){t.bindUIActions()},bindUIActions:function(){s(e).on("wpformsSaved",t.ajaxRequiredCheck),s(e).on("wpformsSaved",t.paymentsEnabledCheck),s(e).on("click","#wpforms-add-fields-"+wpforms_builder_stripe.field_slug,t.stripeKeysCheck),s(e).on("change",".wpforms-field-option-stripe-credit-card .wpforms-field-option-row-sublabel_position select",t.sublabelPositionChange),s(e).on("change",".wpforms-field-option-stripe-credit-card .wpforms-field-option-row-link_email select",t.linkEmailChange),s(e).on("wpformsFieldAdd",t.disableAddCardButton),s(e).on("wpformsFieldDelete",t.enableAddCardButton),s(e).on("wpformsFieldDelete",t.maybeResetLinkEmailField)},ajaxRequiredCheck:function(){s(".wpforms-field.wpforms-field-"+wpforms_builder_stripe.field_slug).length&&!s("#wpforms-panel-field-settings-ajax_submit").is(":checked")&&s.alert({title:wpforms_builder.heads_up,content:wpforms_builder.stripe_ajax_required,icon:"fa fa-exclamation-circle",type:"orange",buttons:{confirm:{text:wpforms_builder.ok,btnClass:"btn-confirm",keys:["enter"]}}})},paymentsEnabledCheck:function(){!s(`.wpforms-field.wpforms-field-${wpforms_builder_stripe.field_slug}:visible`).length||s("#wpforms-panel-field-stripe-enable").is(":checked")||s("#wpforms-panel-field-stripe-enable_one_time").is(":checked")||s("#wpforms-panel-field-stripe-enable_recurring").is(":checked")||s.alert({title:wpforms_builder.heads_up,content:wpforms_builder.payments_enabled_required,icon:"fa fa-exclamation-circle",type:"red",buttons:{confirm:{text:wpforms_builder.ok,btnClass:"btn-confirm",keys:["enter"]}}})},stripeKeysCheck:function(){s(this).hasClass("stripe-keys-required")&&s.alert({title:wpforms_builder.heads_up,content:wpforms_builder.stripe_keys_required,icon:"fa fa-exclamation-circle",type:"orange",buttons:{confirm:{text:wpforms_builder.ok,btnClass:"btn-confirm",keys:["enter"]}}})},disableAddCardButton:function(e,i,r){wpforms_builder_stripe.field_slug===r&&(s("#wpforms-add-fields-"+wpforms_builder_stripe.field_slug).prop("disabled",!0),t.paymentsEnabledCheck())},enableAddCardButton:function(e,i,r){wpforms_builder_stripe.field_slug===r&&s("#wpforms-add-fields-"+wpforms_builder_stripe.field_slug).prop("disabled",!1)},sublabelPositionChange:function(){var e=s(this).parent().data("field-id"),e=s("#wpforms-field-"+e).find(".wpforms-stripe-payment-element");e.toggleClass("above"),e.toggleClass("floating"),e.find("select").val(e.hasClass("above")?"empty":"country")},linkEmailChange:function(){var e=s(this).parent().data("field-id");s("#wpforms-field-"+e).find(".wpforms-stripe-link-email").toggleClass("wpforms-hidden",""!==s(this).val())},maybeResetLinkEmailField:function(e,i,r){"email"===r&&s(".wpforms-field-option-stripe-credit-card .wpforms-field-option-row-link_email select").trigger("change")}};return t})(document,(window,jQuery));WPFormsStripeCardField.init();
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
/* global wpforms_builder, wpforms_builder_stripe */
|
||||
|
||||
// noinspection ES6ConvertVarToLetConst
|
||||
/**
|
||||
* Stripe builder function.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*/
|
||||
// eslint-disable-next-line no-var
|
||||
var WPFormsStripe = window.WPFormsStripe || ( function( document, window, $ ) {
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const app = {
|
||||
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*/
|
||||
init() {
|
||||
$( app.ready );
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialized once the DOM is fully loaded.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*/
|
||||
ready() {
|
||||
if ( ! app.isLegacySettings() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
app.settingsDisplay();
|
||||
app.settingsConditions();
|
||||
|
||||
app.bindUIActions();
|
||||
},
|
||||
|
||||
/**
|
||||
* Process various events as a response to UI interactions.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*/
|
||||
bindUIActions() {
|
||||
$( document )
|
||||
.on( 'wpformsFieldDelete', app.disableNotifications )
|
||||
.on( 'wpformsSaved', app.requiredFieldsCheck )
|
||||
.on( 'wpformsFieldUpdate', app.settingsDisplay )
|
||||
.on( 'wpformsFieldUpdate', app.settingsConditions );
|
||||
|
||||
$( '#wpforms-panel-field-stripe-recurring-email' ).on( 'change', app.resetEmailAlertErrorClass );
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggles visibility of the Stripe settings.
|
||||
*
|
||||
* If a credit card field has been added, then reveal the settings.
|
||||
* Otherwise, hide them.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*/
|
||||
settingsDisplay() {
|
||||
const $alert = $( '#wpforms-stripe-credit-card-alert' );
|
||||
const $content = $( '#stripe-provider' );
|
||||
|
||||
// Check if any Credit Card fields were added to the form.
|
||||
const ccFieldsAdded = wpforms_builder_stripe.field_slugs.filter( function( fieldSlug ) {
|
||||
const $el = $( '.wpforms-field-option-' + fieldSlug );
|
||||
|
||||
return $el.length ? $el : null;
|
||||
} );
|
||||
|
||||
if ( ccFieldsAdded.length ) {
|
||||
$alert.hide();
|
||||
$content.find( '#wpforms-stripe-new-interface-alert, .wpforms-stripe-notice-info, .wpforms-panel-field, .wpforms-conditional-block-panel, h2' ).show();
|
||||
} else {
|
||||
$alert.show();
|
||||
$content.find( '#wpforms-stripe-new-interface-alert, .wpforms-stripe-notice-info, .wpforms-panel-field, .wpforms-conditional-block-panel, h2' ).hide();
|
||||
$content.find( '#wpforms-panel-field-stripe-enable' ).prop( 'checked', false ).trigger( 'change' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggles the visibility of the related settings.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*/
|
||||
settingsConditions() {
|
||||
$( '#wpforms-panel-field-stripe-enable' ).conditions( {
|
||||
conditions: {
|
||||
element: '#wpforms-panel-field-stripe-enable',
|
||||
type: 'checked',
|
||||
operator: 'is',
|
||||
},
|
||||
actions: {
|
||||
if: {
|
||||
element: '.wpforms-panel-content-section-stripe-body',
|
||||
action: 'show',
|
||||
},
|
||||
else: {
|
||||
element: '.wpforms-panel-content-section-stripe-body',
|
||||
action: 'hide',
|
||||
},
|
||||
},
|
||||
effect: 'appear',
|
||||
} );
|
||||
|
||||
$( '#wpforms-panel-field-stripe-recurring-enable' ).conditions( {
|
||||
conditions: {
|
||||
element: '#wpforms-panel-field-stripe-recurring-enable',
|
||||
type: 'checked',
|
||||
operator: 'is',
|
||||
},
|
||||
actions: {
|
||||
if: {
|
||||
element: '#wpforms-panel-field-stripe-recurring-period-wrap,#wpforms-panel-field-stripe-recurring-conditional_logic-wrap,#wpforms-conditional-groups-payments-stripe-recurring,#wpforms-panel-field-stripe-recurring-email-wrap,#wpforms-panel-field-stripe-recurring-name-wrap',
|
||||
action: 'show',
|
||||
},
|
||||
else: {
|
||||
element: '#wpforms-panel-field-stripe-recurring-period-wrap,#wpforms-panel-field-stripe-recurring-conditional_logic-wrap,#wpforms-conditional-groups-payments-stripe-recurring,#wpforms-panel-field-stripe-recurring-email-wrap,#wpforms-panel-field-stripe-recurring-name-wrap',
|
||||
action: 'hide',
|
||||
},
|
||||
},
|
||||
effect: 'appear',
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* On form save notify users about required fields.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*/
|
||||
requiredFieldsCheck() {
|
||||
if (
|
||||
! $( '#wpforms-panel-field-stripe-enable' ).is( ':checked' ) ||
|
||||
! $( '#wpforms-panel-field-stripe-recurring-enable' ).is( ':checked' )
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const $emailField = $( '#wpforms-panel-field-stripe-recurring-email' );
|
||||
|
||||
if ( $emailField.val() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$emailField.addClass( 'wpforms-required-field-error' );
|
||||
|
||||
let alertMessage = wpforms_builder.stripe_recurring_email;
|
||||
|
||||
if ( ! $( '.wpforms-panel-content-section-stripe' ).is( ':visible' ) ) {
|
||||
alertMessage += ' ' + wpforms_builder.stripe_recurring_settings;
|
||||
}
|
||||
|
||||
$.alert( {
|
||||
title: wpforms_builder.stripe_recurring_heading,
|
||||
content: alertMessage,
|
||||
icon: 'fa fa-exclamation-circle',
|
||||
type: 'red',
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: wpforms_builder.ok,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
},
|
||||
},
|
||||
onOpen() {
|
||||
$( '.wpforms-stripe-settings-redirect' ).on( 'click', app.settingsRedirect );
|
||||
},
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Redirect to the settings tab.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
settingsRedirect() {
|
||||
// Open the Stripe settings tab.
|
||||
$( '.wpforms-panel-payments-button' ).trigger( 'click' );
|
||||
$( '.wpforms-panel-sidebar-section-stripe' ).trigger( 'click' );
|
||||
|
||||
// Scroll to the Stripe settings.
|
||||
window.location.href = window.location.pathname + window.location.search + '#wpforms-panel-field-stripe-enable_recurring-wrap';
|
||||
|
||||
// Close the alert.
|
||||
$( this ).closest( '.jconfirm-box' ).find( '.btn-confirm' ).trigger( 'click' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Maybe reset required email field error class.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
resetEmailAlertErrorClass() {
|
||||
$( this ).toggleClass( 'wpforms-required-field-error', ! $( this ).val() );
|
||||
},
|
||||
|
||||
/**
|
||||
* Disable notifications.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*
|
||||
* @param {Object} e Event object.
|
||||
* @param {number} id Field ID.
|
||||
* @param {string} type Field type.
|
||||
*/
|
||||
disableNotifications( e, id, type ) {
|
||||
if ( ! wpforms_builder_stripe.field_slugs.includes( type ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const $notificationWrap = $( '.wpforms-panel-content-section-notifications [id*="-stripe-wrap"]' );
|
||||
|
||||
$notificationWrap.find( 'input[id*="-stripe"]' ).prop( 'checked', false );
|
||||
$notificationWrap.addClass( 'wpforms-hidden' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine is legacy settings is loaded.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*
|
||||
* @return {boolean} True is legacy settings loaded.
|
||||
*/
|
||||
isLegacySettings() {
|
||||
return $( '#wpforms-panel-field-stripe-enable' ).length;
|
||||
},
|
||||
};
|
||||
|
||||
// Provide access to public functions/properties.
|
||||
return app;
|
||||
}( document, window, jQuery ) );
|
||||
|
||||
// Initialize.
|
||||
WPFormsStripe.init();
|
||||
Vendored
Executable
+1
@@ -0,0 +1 @@
|
||||
var WPFormsStripe=window.WPFormsStripe||((e,i,n)=>{let r={init(){n(r.ready)},ready(){r.isLegacySettings()&&(r.settingsDisplay(),r.settingsConditions(),r.bindUIActions())},bindUIActions(){n(e).on("wpformsFieldDelete",r.disableNotifications).on("wpformsSaved",r.requiredFieldsCheck).on("wpformsFieldUpdate",r.settingsDisplay).on("wpformsFieldUpdate",r.settingsConditions),n("#wpforms-panel-field-stripe-recurring-email").on("change",r.resetEmailAlertErrorClass)},settingsDisplay(){var e=n("#wpforms-stripe-credit-card-alert"),i=n("#stripe-provider");wpforms_builder_stripe.field_slugs.filter(function(e){e=n(".wpforms-field-option-"+e);return e.length?e:null}).length?(e.hide(),i.find("#wpforms-stripe-new-interface-alert, .wpforms-stripe-notice-info, .wpforms-panel-field, .wpforms-conditional-block-panel, h2").show()):(e.show(),i.find("#wpforms-stripe-new-interface-alert, .wpforms-stripe-notice-info, .wpforms-panel-field, .wpforms-conditional-block-panel, h2").hide(),i.find("#wpforms-panel-field-stripe-enable").prop("checked",!1).trigger("change"))},settingsConditions(){n("#wpforms-panel-field-stripe-enable").conditions({conditions:{element:"#wpforms-panel-field-stripe-enable",type:"checked",operator:"is"},actions:{if:{element:".wpforms-panel-content-section-stripe-body",action:"show"},else:{element:".wpforms-panel-content-section-stripe-body",action:"hide"}},effect:"appear"}),n("#wpforms-panel-field-stripe-recurring-enable").conditions({conditions:{element:"#wpforms-panel-field-stripe-recurring-enable",type:"checked",operator:"is"},actions:{if:{element:"#wpforms-panel-field-stripe-recurring-period-wrap,#wpforms-panel-field-stripe-recurring-conditional_logic-wrap,#wpforms-conditional-groups-payments-stripe-recurring,#wpforms-panel-field-stripe-recurring-email-wrap,#wpforms-panel-field-stripe-recurring-name-wrap",action:"show"},else:{element:"#wpforms-panel-field-stripe-recurring-period-wrap,#wpforms-panel-field-stripe-recurring-conditional_logic-wrap,#wpforms-conditional-groups-payments-stripe-recurring,#wpforms-panel-field-stripe-recurring-email-wrap,#wpforms-panel-field-stripe-recurring-name-wrap",action:"hide"}},effect:"appear"})},requiredFieldsCheck(){if(n("#wpforms-panel-field-stripe-enable").is(":checked")&&n("#wpforms-panel-field-stripe-recurring-enable").is(":checked")){var i=n("#wpforms-panel-field-stripe-recurring-email");if(!i.val()){i.addClass("wpforms-required-field-error");let e=wpforms_builder.stripe_recurring_email;n(".wpforms-panel-content-section-stripe").is(":visible")||(e+=" "+wpforms_builder.stripe_recurring_settings),n.alert({title:wpforms_builder.stripe_recurring_heading,content:e,icon:"fa fa-exclamation-circle",type:"red",buttons:{confirm:{text:wpforms_builder.ok,btnClass:"btn-confirm",keys:["enter"]}},onOpen(){n(".wpforms-stripe-settings-redirect").on("click",r.settingsRedirect)}})}}},settingsRedirect(){n(".wpforms-panel-payments-button").trigger("click"),n(".wpforms-panel-sidebar-section-stripe").trigger("click"),i.location.href=i.location.pathname+i.location.search+"#wpforms-panel-field-stripe-enable_recurring-wrap",n(this).closest(".jconfirm-box").find(".btn-confirm").trigger("click")},resetEmailAlertErrorClass(){n(this).toggleClass("wpforms-required-field-error",!n(this).val())},disableNotifications(e,i,r){wpforms_builder_stripe.field_slugs.includes(r)&&((r=n('.wpforms-panel-content-section-notifications [id*="-stripe-wrap"]')).find('input[id*="-stripe"]').prop("checked",!1),r.addClass("wpforms-hidden"))},isLegacySettings(){return n("#wpforms-panel-field-stripe-enable").length}};return r})(document,window,jQuery);WPFormsStripe.init();
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
/* global wpforms_admin_settings_stripe, wpforms_admin, wpf */
|
||||
|
||||
/**
|
||||
* Stripe integration settings script.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*/
|
||||
|
||||
const WPFormsSettingsStripe = window.WPFormsSettingsStripe || ( function( document, window, $ ) {
|
||||
/**
|
||||
* Elements holder.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const el = {};
|
||||
|
||||
/**
|
||||
* Runtime variables.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const vars = {
|
||||
alertTitle: wpforms_admin.heads_up,
|
||||
alertContent: wpforms_admin_settings_stripe.mode_update,
|
||||
ok: wpforms_admin.ok,
|
||||
hideClassName: 'wpforms-hide',
|
||||
};
|
||||
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*/
|
||||
const app = {
|
||||
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*/
|
||||
init() {
|
||||
$( app.ready );
|
||||
},
|
||||
|
||||
/**
|
||||
* Document ready.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*/
|
||||
ready() {
|
||||
app.setup();
|
||||
app.bindEvents();
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup. Prepare some variables.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*/
|
||||
setup() {
|
||||
// Cache DOM elements.
|
||||
el.$wrapper = $( '.wpforms-admin-content-payments' );
|
||||
el.$liveConnectionBlock = $( '.wpforms-stripe-connection-status-live' );
|
||||
el.$testConnectionBlock = $( '.wpforms-stripe-connection-status-test' );
|
||||
el.$testModeCheckbox = $( '#wpforms-setting-stripe-test-mode' );
|
||||
el.copyButton = $( '#wpforms-setting-row-stripe-webhooks-endpoint-set .wpforms-copy-to-clipboard' );
|
||||
el.webhookEndpointUrl = $( 'input#wpforms-stripe-webhook-endpoint-url' );
|
||||
el.webhookMethod = $( 'input[name="stripe-webhooks-communication"]' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Bind events.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*/
|
||||
bindEvents() {
|
||||
el.$wrapper
|
||||
.on( 'change', '#wpforms-setting-stripe-test-mode', app.triggerModeSwitchAlert );
|
||||
el.copyButton
|
||||
.on( 'click', function( e ) {
|
||||
wpf.copyValueToClipboard( e, $( this ), el.webhookEndpointUrl );
|
||||
} );
|
||||
el.webhookMethod
|
||||
.on( 'change', app.onMethodChange );
|
||||
},
|
||||
|
||||
/**
|
||||
* Conditionally show Stripe mode switch warning.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*/
|
||||
triggerModeSwitchAlert() {
|
||||
if ( el.$testModeCheckbox.is( ':checked' ) ) {
|
||||
el.$liveConnectionBlock.addClass( vars.hideClassName );
|
||||
el.$testConnectionBlock.removeClass( vars.hideClassName );
|
||||
} else {
|
||||
el.$testConnectionBlock.addClass( vars.hideClassName );
|
||||
el.$liveConnectionBlock.removeClass( vars.hideClassName );
|
||||
}
|
||||
|
||||
if ( $( '#wpforms-setting-row-stripe-connection-status .wpforms-connected' ).is( ':visible' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.alert( {
|
||||
title: vars.alertTitle,
|
||||
content: vars.alertContent,
|
||||
icon: 'fa fa-exclamation-circle',
|
||||
type: 'orange',
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: vars.ok,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
},
|
||||
},
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy webhooks endpoint URL to clipboard.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*
|
||||
* @deprecated 1.9.5 Changed to the wpf.copyWebhooksEndpoint().
|
||||
*
|
||||
* @param {Object} event Event object.
|
||||
*/
|
||||
copyWebhooksEndpoint( event ) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn( 'WARNING! Function "WPFormsSettingsStripe.copyWebhooksEndpoint()" has been deprecated! Use wpf.copyWebhooksEndpoint() instead.' );
|
||||
|
||||
wpf.copyValueToClipboard( event, $( this ), el.webhookEndpointUrl );
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the endpoint URL.
|
||||
*
|
||||
* @since 1.8.4
|
||||
*/
|
||||
onMethodChange() {
|
||||
const checked = el.webhookMethod.filter( ':checked' ).val(),
|
||||
newUrl = wpforms_admin_settings_stripe.webhook_urls[ checked ];
|
||||
|
||||
el.webhookEndpointUrl.val( newUrl );
|
||||
},
|
||||
};
|
||||
|
||||
// Provide access to public functions/properties.
|
||||
return app;
|
||||
}( document, window, jQuery ) );
|
||||
|
||||
// Initialize.
|
||||
WPFormsSettingsStripe.init();
|
||||
Vendored
Executable
+1
@@ -0,0 +1 @@
|
||||
let WPFormsSettingsStripe=window.WPFormsSettingsStripe||(o=>{let t={},e={alertTitle:wpforms_admin.heads_up,alertContent:wpforms_admin_settings_stripe.mode_update,ok:wpforms_admin.ok,hideClassName:"wpforms-hide"},n={init(){o(n.ready)},ready(){n.setup(),n.bindEvents()},setup(){t.$wrapper=o(".wpforms-admin-content-payments"),t.$liveConnectionBlock=o(".wpforms-stripe-connection-status-live"),t.$testConnectionBlock=o(".wpforms-stripe-connection-status-test"),t.$testModeCheckbox=o("#wpforms-setting-stripe-test-mode"),t.copyButton=o("#wpforms-setting-row-stripe-webhooks-endpoint-set .wpforms-copy-to-clipboard"),t.webhookEndpointUrl=o("input#wpforms-stripe-webhook-endpoint-url"),t.webhookMethod=o('input[name="stripe-webhooks-communication"]')},bindEvents(){t.$wrapper.on("change","#wpforms-setting-stripe-test-mode",n.triggerModeSwitchAlert),t.copyButton.on("click",function(e){wpf.copyValueToClipboard(e,o(this),t.webhookEndpointUrl)}),t.webhookMethod.on("change",n.onMethodChange)},triggerModeSwitchAlert(){(t.$testModeCheckbox.is(":checked")?(t.$liveConnectionBlock.addClass(e.hideClassName),t.$testConnectionBlock):(t.$testConnectionBlock.addClass(e.hideClassName),t.$liveConnectionBlock)).removeClass(e.hideClassName),o("#wpforms-setting-row-stripe-connection-status .wpforms-connected").is(":visible")||o.alert({title:e.alertTitle,content:e.alertContent,icon:"fa fa-exclamation-circle",type:"orange",buttons:{confirm:{text:e.ok,btnClass:"btn-confirm",keys:["enter"]}}})},copyWebhooksEndpoint(e){console.warn('WARNING! Function "WPFormsSettingsStripe.copyWebhooksEndpoint()" has been deprecated! Use wpf.copyWebhooksEndpoint() instead.'),wpf.copyValueToClipboard(e,o(this),t.webhookEndpointUrl)},onMethodChange(){var e=t.webhookMethod.filter(":checked").val(),e=wpforms_admin_settings_stripe.webhook_urls[e];t.webhookEndpointUrl.val(e)}};return n})((document,window,jQuery));WPFormsSettingsStripe.init();
|
||||
+472
@@ -0,0 +1,472 @@
|
||||
/* global Stripe, wpforms, wpforms_settings, wpforms_stripe, WPForms */
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* WPForms Stripe Elements function.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*/
|
||||
var WPFormsStripeElements = window.WPFormsStripeElements || ( function( document, window, $ ) {
|
||||
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*
|
||||
* @type {object}
|
||||
*/
|
||||
const app = {
|
||||
|
||||
stripe: null,
|
||||
|
||||
/**
|
||||
* Number of page locked to switch.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*
|
||||
* @type {int}
|
||||
*/
|
||||
lockedPageToSwitch: 0,
|
||||
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*/
|
||||
init: function() {
|
||||
|
||||
app.stripe = Stripe( // eslint-disable-line new-cap
|
||||
wpforms_stripe.publishable_key,
|
||||
{ 'locale': wpforms_stripe.data.element_locale }
|
||||
);
|
||||
|
||||
$( document ).on( 'wpformsReady', function() {
|
||||
$( '.wpforms-stripe form' )
|
||||
.filter( ( _, form ) => typeof $( form ).data( 'formid' ) === 'number' ) // filter out forms which are locked (formid changed to 'locked-...').
|
||||
.each( app.setupStripeForm );
|
||||
} );
|
||||
|
||||
$( document ).on( 'wpformsBeforePageChange', app.pageChange );
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup and configure a Stripe form.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*/
|
||||
setupStripeForm: function() {
|
||||
|
||||
let $form = $( this );
|
||||
|
||||
app.updateFormSubmitHandler( $form );
|
||||
|
||||
$form.on( 'wpformsAjaxSubmitActionRequired', app.handleCardActionCallback );
|
||||
|
||||
app.updateCardElementStylesModern( $form );
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup, mount and configure Stripe Card Element.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*
|
||||
* @param {jQuery} $form Form element.
|
||||
* @param {object} formValidator jQuery Validator object.
|
||||
*
|
||||
* @returns {card|void} Stripe Card element.
|
||||
*/
|
||||
setupCardElement: function( $form, formValidator ) {
|
||||
|
||||
const $hiddenInput = $form.find( '.wpforms-stripe-credit-card-hidden-input' );
|
||||
|
||||
if ( ! $hiddenInput || $hiddenInput.length === 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
let cardElement = $hiddenInput.data( 'stripe-element' );
|
||||
|
||||
if ( cardElement ) {
|
||||
return cardElement;
|
||||
}
|
||||
|
||||
let style = wpforms_stripe.data.element_style;
|
||||
|
||||
if ( $.isEmptyObject( style ) ) {
|
||||
style = app.getElementStyleDefault( $hiddenInput );
|
||||
}
|
||||
|
||||
let cardSettings = {
|
||||
classes : wpforms_stripe.data.element_classes,
|
||||
hidePostalCode: true,
|
||||
style : style,
|
||||
};
|
||||
|
||||
cardElement = app.stripe.elements().create( 'card', cardSettings );
|
||||
|
||||
cardElement.mount( $form.find( '.wpforms-field-stripe-credit-card-cardnumber' ).get( 0 ) );
|
||||
|
||||
cardElement.on( 'change', function( e ) {
|
||||
|
||||
if ( ! e.error ) {
|
||||
formValidator.hideThese( formValidator.errorsFor( $hiddenInput.get( 0 ) ) );
|
||||
return;
|
||||
}
|
||||
|
||||
let message = e.error.message;
|
||||
|
||||
if ( 'incomplete_number' === e.error.code || 'invalid_number' === e.error.code ) {
|
||||
message = wpforms_settings.val_creditcard;
|
||||
}
|
||||
|
||||
app.displayStripeError( $form, message );
|
||||
} );
|
||||
|
||||
cardElement.on( 'focus', function() {
|
||||
$( document ).trigger( 'wpformsStripePaymentElementFocus', [ $form ] );
|
||||
} );
|
||||
|
||||
$hiddenInput.data( 'stripe-element', cardElement );
|
||||
|
||||
return cardElement;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get default styles for card settings.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*
|
||||
* @param {jQuery} $hiddenInput Input element.
|
||||
*
|
||||
* @returns {object|void} Base styles.
|
||||
*/
|
||||
getElementStyleDefault: function( $hiddenInput ) {
|
||||
|
||||
if ( ! $hiddenInput || $hiddenInput.length === 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const textColor = $hiddenInput.css( 'color' );
|
||||
const fontSize = $hiddenInput.css( 'font-size' );
|
||||
|
||||
const style = {
|
||||
base: {
|
||||
fontSize,
|
||||
color: textColor,
|
||||
'::placeholder': {
|
||||
color: textColor,
|
||||
fontSize,
|
||||
},
|
||||
},
|
||||
invalid: {
|
||||
color: textColor,
|
||||
},
|
||||
};
|
||||
|
||||
let fontFamily = $hiddenInput.css( 'font-family' );
|
||||
|
||||
const regExp = /[“”<>!@$%^&*=~`|{}[\]]/;
|
||||
|
||||
if ( regExp.test( fontFamily ) || fontFamily.indexOf( 'MS Shell Dlg' ) !== -1 ) {
|
||||
fontFamily = $( 'p' ).css( 'font-family' );
|
||||
}
|
||||
|
||||
if ( ! regExp.test( fontFamily ) ) {
|
||||
style.base.fontFamily = fontFamily;
|
||||
style.base[ '::placeholder' ].fontFamily = fontFamily;
|
||||
}
|
||||
|
||||
return style;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update submitHandler for the forms containing Stripe.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*
|
||||
* @param {jQuery} $form Form element.
|
||||
*/
|
||||
updateFormSubmitHandler: function( $form ) {
|
||||
|
||||
let formValidator = $form.validate(),
|
||||
formSubmitHandler = formValidator.settings.submitHandler,
|
||||
cardElement = app.setupCardElement( $form, formValidator ),
|
||||
$stripeDiv = $form.find( '.wpforms-field-stripe-credit-card-cardnumber' );
|
||||
|
||||
// Replace the default submit handler.
|
||||
formValidator.settings.submitHandler = function() {
|
||||
|
||||
let valid = $form.validate().form(),
|
||||
ccEmpty = $stripeDiv.hasClass( wpforms_stripe.data.element_classes.empty ),
|
||||
ccRequired = $stripeDiv.data( 'required' ),
|
||||
condHidden = $stripeDiv.closest( '.wpforms-field-stripe-credit-card' ).hasClass( 'wpforms-conditional-hide' ),
|
||||
processCard = false;
|
||||
|
||||
if ( ! condHidden ) {
|
||||
processCard = ccRequired || ( ! ccEmpty && ! ccRequired );
|
||||
}
|
||||
|
||||
if ( valid && processCard ) {
|
||||
|
||||
$form.find( '.wpforms-submit' ).prop( 'disabled', true );
|
||||
app.createPaymentMethod( $form, cardElement, ccRequired, formSubmitHandler );
|
||||
|
||||
} else if ( valid ) {
|
||||
|
||||
// Form is valid, however no credit card to process.
|
||||
$form.find( '.wpforms-submit' ).prop( 'disabled', false );
|
||||
return formSubmitHandler( $form );
|
||||
|
||||
} else {
|
||||
|
||||
$form.find( '.wpforms-submit' ).prop( 'disabled', false );
|
||||
$form.validate().cancelSubmit = true;
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a PaymentMethod out of card details provided.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*
|
||||
* @param {jQuery} $form Form element.
|
||||
* @param {card} cardElement Stripe Card element.
|
||||
* @param {boolean} ccRequired Card field is required.
|
||||
* @param {Function} formSubmitHandler jQuery Validation SubmitHandler function.
|
||||
*/
|
||||
createPaymentMethod: function( $form, cardElement, ccRequired, formSubmitHandler ) {
|
||||
|
||||
app.stripe.createPaymentMethod( 'card', cardElement, {
|
||||
billing_details: {
|
||||
name: $form.find( '.wpforms-field-stripe-credit-card-cardname' ).val(),
|
||||
},
|
||||
} ).then( function( result ) {
|
||||
|
||||
if ( result.error && ccRequired ) {
|
||||
$form.find( '.wpforms-submit' ).prop( 'disabled', false );
|
||||
app.displayStripeError( $form, result.error.message );
|
||||
$form.validate().cancelSubmit = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! result.error ) {
|
||||
$form.find( '.wpforms-stripe-payment-method-id' ).remove();
|
||||
if ( result.paymentMethod ) {
|
||||
$form.append( '<input type="hidden" class="wpforms-stripe-payment-method-id" name="wpforms[payment_method_id]" value="' + result.paymentMethod.id + '">' );
|
||||
}
|
||||
}
|
||||
|
||||
formSubmitHandler( $form );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle 'action_required' server response.
|
||||
*
|
||||
* @param {object} e Event object.
|
||||
* @param {object} json Data returned form a server.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*/
|
||||
handleCardActionCallback: function( e, json ) {
|
||||
|
||||
const $form = $( this );
|
||||
|
||||
if ( json.success && json.data.action_required ) {
|
||||
app.stripe.handleCardPayment(
|
||||
json.data.payment_intent_client_secret,
|
||||
{
|
||||
// eslint-disable-next-line camelcase
|
||||
payment_method: json.data.payment_method_id,
|
||||
}
|
||||
).then( function( result ) {
|
||||
app.handleCardPaymentCallback( $form, result );
|
||||
} );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback for Stripe 'handleCardPayment' method.
|
||||
*
|
||||
* @param {jQuery} $form Form element.
|
||||
* @param {Object} result Data returned by 'handleCardPayment'.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*/
|
||||
handleCardPaymentCallback( $form, result ) {
|
||||
if ( result.error ) {
|
||||
wpforms.restoreSubmitButton( $form, $form.closest( '.wpforms-container' ) );
|
||||
$form.find( '.wpforms-field-stripe-credit-card-cardnumber' ).addClass( wpforms_stripe.data.element_classes.invalid );
|
||||
app.displayStripeError( $form, result.error.message );
|
||||
} else if ( result.paymentIntent && 'succeeded' === result.paymentIntent.status ) {
|
||||
$form.find( '.wpforms-stripe-payment-method-id' ).remove();
|
||||
$form.find( '.wpforms-stripe-payment-intent-id' ).remove();
|
||||
$form.append( '<input type="hidden" class="wpforms-stripe-payment-intent-id" name="wpforms[payment_intent_id]" value="' + result.paymentIntent.id + '">' );
|
||||
wpforms.formSubmitAjax( $form );
|
||||
} else {
|
||||
wpforms.restoreSubmitButton( $form, $form.closest( '.wpforms-container' ) );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Display a field error using jQuery Validate library.
|
||||
*
|
||||
* @param {jQuery} $form Form element.
|
||||
* @param {object} message Error message.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*/
|
||||
displayStripeError: function( $form, message ) {
|
||||
|
||||
const fieldName = $form.find( '.wpforms-stripe-credit-card-hidden-input' ).attr( 'name' ),
|
||||
$stripeDiv = $form.find( '.wpforms-field-stripe-credit-card-cardnumber' );
|
||||
let errors = {};
|
||||
|
||||
errors[fieldName] = message;
|
||||
|
||||
wpforms.displayFormAjaxFieldErrors( $form, errors );
|
||||
|
||||
// Switch page for the multipage form.
|
||||
if ( ! $stripeDiv.is( ':visible' ) && $form.find( '.wpforms-page-indicator-steps' ).length > 0 ) {
|
||||
// Empty $json object needed to change the page to the first one.
|
||||
wpforms.setCurrentPage( $form, {} );
|
||||
}
|
||||
|
||||
wpforms.scrollToError( $stripeDiv );
|
||||
},
|
||||
|
||||
/**
|
||||
* Unblock the AJAX form.
|
||||
*
|
||||
* @since 1.8.2
|
||||
* @deprecated 1.9.5
|
||||
*
|
||||
* @param {jQuery} $form Form element.
|
||||
*/
|
||||
formAjaxUnblock( $form ) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn( 'WARNING! Function "WPFormsStripeElements.formAjaxUnblock()" has been deprecated, please use the new "wpforms.restoreSubmitButton()" function instead!' );
|
||||
|
||||
wpforms.restoreSubmitButton( $form, $form.closest( '.wpforms-container' ) );
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback for a page changing.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*
|
||||
* @param {Event} event Event.
|
||||
* @param {int} currentPage Current page.
|
||||
* @param {jQuery} $form Current form.
|
||||
* @param {string} action The navigation action.
|
||||
*/
|
||||
pageChange: function( event, currentPage, $form, action ) {
|
||||
|
||||
const $stripeDiv = $form.find( '.wpforms-field-stripe-credit-card-cardnumber' ),
|
||||
ccComplete = $stripeDiv.hasClass( wpforms_stripe.data.element_classes.complete ),
|
||||
ccEmpty = $stripeDiv.hasClass( wpforms_stripe.data.element_classes.empty ),
|
||||
ccInvalid = $stripeDiv.hasClass( wpforms_stripe.data.element_classes.invalid );
|
||||
|
||||
// Stop navigation through page break pages.
|
||||
if (
|
||||
! $stripeDiv.is( ':visible' ) ||
|
||||
( ! $stripeDiv.data( 'required' ) && ccEmpty ) ||
|
||||
( app.lockedPageToSwitch && app.lockedPageToSwitch !== currentPage ) ||
|
||||
action === 'prev'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ccComplete ) {
|
||||
$stripeDiv.find( '.wpforms-error' ).remove();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
app.lockedPageToSwitch = currentPage;
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
if ( ccInvalid ) {
|
||||
return;
|
||||
}
|
||||
|
||||
app.displayStripeError( $form, wpforms_stripe.i18n.empty_details );
|
||||
},
|
||||
|
||||
/**
|
||||
* Get CSS property value.
|
||||
* In case of exception return empty string.
|
||||
*
|
||||
* @since 1.8.6
|
||||
*
|
||||
* @param {jQuery} $element Element.
|
||||
* @param {string} property Property.
|
||||
*
|
||||
* @return {string} Property value.
|
||||
*/
|
||||
getCssPropertyValue( $element, property ) {
|
||||
try {
|
||||
return $element.css( property );
|
||||
} catch ( e ) {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Update Card Element styles in Modern Markup mode.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*
|
||||
* @param {jQuery} $form Form object.
|
||||
*/
|
||||
updateCardElementStylesModern( $form ) {
|
||||
// Should work only in Modern Markup mode.
|
||||
if ( ! window.WPForms || ! WPForms.FrontendModern || ! $.isEmptyObject( wpforms_stripe.data.element_style ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! $form || $form.length === 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$form.find( '.wpforms-stripe-credit-card-hidden-input' ).each( function() {
|
||||
const $hiddenInput = $( this );
|
||||
const cardElement = $hiddenInput.data( 'stripe-element' );
|
||||
|
||||
const inputStyle = {
|
||||
fontSize: app.getCssPropertyValue( $hiddenInput, 'font-size' ),
|
||||
colorText: app.getCssPropertyValue( $hiddenInput, 'color' ),
|
||||
};
|
||||
|
||||
if ( ! cardElement ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const styles = {
|
||||
base: {
|
||||
color: inputStyle.colorText,
|
||||
fontSize: inputStyle.fontSize,
|
||||
'::placeholder': {
|
||||
color: WPForms.FrontendModern.getColorWithOpacity( inputStyle.colorText, '0.5' ),
|
||||
fontSize: inputStyle.fontSize,
|
||||
},
|
||||
},
|
||||
invalid: {
|
||||
color: inputStyle.colorText,
|
||||
},
|
||||
};
|
||||
|
||||
cardElement.update( { style: styles } );
|
||||
} );
|
||||
},
|
||||
};
|
||||
|
||||
// Provide access to public functions/properties.
|
||||
return app;
|
||||
|
||||
}( document, window, jQuery ) );
|
||||
|
||||
// Initialize.
|
||||
WPFormsStripeElements.init();
|
||||
Vendored
Executable
+1
File diff suppressed because one or more lines are too long
Executable
+1283
File diff suppressed because it is too large
Load Diff
Vendored
Executable
+1
File diff suppressed because one or more lines are too long
+74
@@ -0,0 +1,74 @@
|
||||
/* global wpforms_woocommerce_notifications */
|
||||
|
||||
/**
|
||||
* WooCommerce Notifications integration script.
|
||||
*
|
||||
* @since 1.8.9
|
||||
*/
|
||||
const WPFormsWoocommerceNotifications = window.WPFormsWoocommerceNotifications || ( function( document, window, $ ) {
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.8.9
|
||||
*/
|
||||
const app = {
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.8.9
|
||||
*/
|
||||
init() {
|
||||
$( app.ready );
|
||||
},
|
||||
|
||||
/**
|
||||
* Document ready.
|
||||
*
|
||||
* @since 1.8.9
|
||||
*/
|
||||
ready() {
|
||||
app.events();
|
||||
},
|
||||
|
||||
/**
|
||||
* Events.
|
||||
*
|
||||
* @since 1.8.9
|
||||
*/
|
||||
events() {
|
||||
$( '#wpforms-woocommerce-close' ).on( 'click', app.dismiss );
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide notification.
|
||||
*
|
||||
* @since 1.8.9
|
||||
*/
|
||||
dismiss() {
|
||||
const $btn = $( this );
|
||||
const $notification = $btn.closest( '.wpforms-woocommerce-notification' );
|
||||
|
||||
$notification.remove();
|
||||
|
||||
const data = {
|
||||
action: 'wpforms_woocommerce_dismiss',
|
||||
nonce: wpforms_woocommerce_notifications.nonce,
|
||||
};
|
||||
|
||||
$.post( wpforms_woocommerce_notifications.ajax_url, data, function( res ) {
|
||||
if ( ! res.success ) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log( res );
|
||||
}
|
||||
} ).fail( function( xhr ) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log( xhr.responseText );
|
||||
} );
|
||||
},
|
||||
};
|
||||
|
||||
return app;
|
||||
}( document, window, jQuery ) );
|
||||
|
||||
// Initialize.
|
||||
WPFormsWoocommerceNotifications.init();
|
||||
Vendored
Executable
+1
@@ -0,0 +1 @@
|
||||
let WPFormsWoocommerceNotifications=window.WPFormsWoocommerceNotifications||(e=>{let o={init(){e(o.ready)},ready(){o.events()},events(){e("#wpforms-woocommerce-close").on("click",o.dismiss)},dismiss(){e(this).closest(".wpforms-woocommerce-notification").remove();var o={action:"wpforms_woocommerce_dismiss",nonce:wpforms_woocommerce_notifications.nonce};e.post(wpforms_woocommerce_notifications.ajax_url,o,function(o){o.success||console.log(o)}).fail(function(o){console.log(o.responseText)})}};return o})((document,window,jQuery));WPFormsWoocommerceNotifications.init();
|
||||
@@ -0,0 +1,165 @@
|
||||
/* global wpformsWpcodeVars, List, wpforms_admin */
|
||||
|
||||
/**
|
||||
* @param wpformsWpcodeVars.installing_text
|
||||
*/
|
||||
|
||||
// noinspection ES6ConvertVarToLetConst
|
||||
/**
|
||||
* WPCode integration script.
|
||||
*
|
||||
* @since 1.8.5
|
||||
*/
|
||||
// eslint-disable-next-line no-var
|
||||
var WPFormsWPCode = window.WPFormsWPCode || ( function( document, window, $ ) {
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.8.5
|
||||
*/
|
||||
const app = {
|
||||
|
||||
/**
|
||||
* Blue spinner HTML.
|
||||
*
|
||||
* @since 1.8.5
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
spinnerBlue: '<i class="wpforms-loading-spinner wpforms-loading-blue wpforms-loading-inline"></i>',
|
||||
|
||||
/**
|
||||
* White spinner HTML.
|
||||
*
|
||||
* @since 1.8.5
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
spinnerWhite: '<i class="wpforms-loading-spinner wpforms-loading-white wpforms-loading-inline"></i>',
|
||||
|
||||
/**
|
||||
* List.js object.
|
||||
*
|
||||
* @since 1.8.5
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
snippetSearch: null,
|
||||
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.8.5
|
||||
*/
|
||||
init() {
|
||||
$( app.ready );
|
||||
},
|
||||
|
||||
/**
|
||||
* Document ready.
|
||||
*
|
||||
* @since 1.8.5
|
||||
*/
|
||||
ready() {
|
||||
app.snippetSearch = new List(
|
||||
'wpforms-wpcode-snippets-list',
|
||||
{
|
||||
valueNames: [ 'wpforms-wpcode-snippet-title' ],
|
||||
}
|
||||
);
|
||||
app.events();
|
||||
},
|
||||
|
||||
/**
|
||||
* Events.
|
||||
*
|
||||
* @since 1.8.5
|
||||
*/
|
||||
events() {
|
||||
$( '.wpforms-wpcode-snippet-button' ).on( 'click', app.installSnippet );
|
||||
|
||||
$( '.wpforms-wpcode-popup-button' ).on( 'click', app.installPlugin );
|
||||
|
||||
$( '#wpforms-wpcode-snippet-search' ).on( 'keyup search', function() {
|
||||
app.searchSnippet( this );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Install snippet.
|
||||
*
|
||||
* @since 1.8.5
|
||||
*/
|
||||
installSnippet() {
|
||||
const $button = $( this );
|
||||
|
||||
if ( $button.data( 'action' ) === 'edit' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const originalWidth = $button.width();
|
||||
const $badge = $button.prev( '.wpforms-wpcode-snippet-badge' );
|
||||
|
||||
$badge.addClass( 'wpforms-wpcode-installing-in-progress' ).text( wpformsWpcodeVars.installing_text );
|
||||
$button.width( originalWidth ).html( app.spinnerBlue );
|
||||
},
|
||||
|
||||
/**
|
||||
* Search snippet.
|
||||
*
|
||||
* @param {Object} searchField The search field html element.
|
||||
* @since 1.8.5
|
||||
*/
|
||||
searchSnippet( searchField ) {
|
||||
const searchTerm = $( searchField ).val();
|
||||
const searchResults = app.snippetSearch.search( searchTerm );
|
||||
const $noResultsMessage = $( '#wpforms-wpcode-no-results' );
|
||||
|
||||
if ( searchResults.length === 0 ) {
|
||||
$noResultsMessage.show();
|
||||
} else {
|
||||
$noResultsMessage.hide();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Install or activate WPCode plugin by button click.
|
||||
*
|
||||
* @since 1.8.5
|
||||
*/
|
||||
installPlugin() {
|
||||
const $btn = $( this );
|
||||
|
||||
if ( $btn.hasClass( 'disabled' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const action = $btn.attr( 'data-action' ),
|
||||
plugin = $btn.attr( 'data-plugin' ),
|
||||
// eslint-disable-next-line camelcase
|
||||
args = JSON.stringify( { overwrite_package: true } ),
|
||||
ajaxAction = action === 'activate' ? 'wpforms_activate_addon' : 'wpforms_install_addon';
|
||||
|
||||
// Fix original button width, add spinner and disable it.
|
||||
$btn.width( $btn.width() ).html( app.spinnerWhite ).addClass( 'disabled' );
|
||||
|
||||
const data = {
|
||||
action: ajaxAction,
|
||||
nonce: wpforms_admin.nonce,
|
||||
plugin,
|
||||
args,
|
||||
type: 'plugin',
|
||||
};
|
||||
|
||||
$.post( wpforms_admin.ajax_url, data )
|
||||
.done( function() {
|
||||
location.reload();
|
||||
} );
|
||||
},
|
||||
};
|
||||
|
||||
return app;
|
||||
}( document, window, jQuery ) );
|
||||
|
||||
// Initialize.
|
||||
WPFormsWPCode.init();
|
||||
+1
@@ -0,0 +1 @@
|
||||
var WPFormsWPCode=window.WPFormsWPCode||(t=>{let p={spinnerBlue:'<i class="wpforms-loading-spinner wpforms-loading-blue wpforms-loading-inline"></i>',spinnerWhite:'<i class="wpforms-loading-spinner wpforms-loading-white wpforms-loading-inline"></i>',snippetSearch:null,init(){t(p.ready)},ready(){p.snippetSearch=new List("wpforms-wpcode-snippets-list",{valueNames:["wpforms-wpcode-snippet-title"]}),p.events()},events(){t(".wpforms-wpcode-snippet-button").on("click",p.installSnippet),t(".wpforms-wpcode-popup-button").on("click",p.installPlugin),t("#wpforms-wpcode-snippet-search").on("keyup search",function(){p.searchSnippet(this)})},installSnippet(){var i,n=t(this);"edit"!==n.data("action")&&(i=n.width(),n.prev(".wpforms-wpcode-snippet-badge").addClass("wpforms-wpcode-installing-in-progress").text(wpformsWpcodeVars.installing_text),n.width(i).html(p.spinnerBlue))},searchSnippet(i){var i=t(i).val(),i=p.snippetSearch.search(i),n=t("#wpforms-wpcode-no-results");0===i.length?n.show():n.hide()},installPlugin(){var i,n,e,s=t(this);s.hasClass("disabled")||(e=s.attr("data-action"),i=s.attr("data-plugin"),n=JSON.stringify({overwrite_package:!0}),e="activate"===e?"wpforms_activate_addon":"wpforms_install_addon",s.width(s.width()).html(p.spinnerWhite).addClass("disabled"),s={action:e,nonce:wpforms_admin.nonce,plugin:i,args:n,type:"plugin"},t.post(wpforms_admin.ajax_url,s).done(function(){location.reload()}))}};return p})((document,window,jQuery));WPFormsWPCode.init();
|
||||
Reference in New Issue
Block a user