diff --git a/bin/zip-plugin.sh b/bin/zip-plugin.sh index f18e14e320..a24dbb5b5d 100755 --- a/bin/zip-plugin.sh +++ b/bin/zip-plugin.sh @@ -129,7 +129,8 @@ zip -r $zipname $destination \ -x "*/eslint-rules/*" \ -x "*/phpcs-sniffs/*" \ -x "$source/venv/*" \ - -x "formidable/resources/*" + -x "formidable/resources/*" \ + -x "formidable-pro/resources/*" if [ ! -z "$3" ]; then rm -rf $destination diff --git a/changelog.txt b/changelog.txt index 592235fb26..ebe04d8bbd 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,4 +1,13 @@ == Changelog == += 6.31 = +* New: Formidable now supports PayPal Commerce. This is built into the Lite plugin. To get started, go to Global Settings > Payments > PayPal to connect your business account. +* New: Form action settings have been redesigned. +* New: Payment actions now use buttons to toggle the selected gateway, and gateway is the top action setting. Note that when custom gateways or Authorize.Net are active, gateways will still revert to the old settings. +* New: The Collect a Payment action icon is now hidden by default, replaced by individual icons for Stripe, Square, and PayPal Commerce. If other gateways are active, Collect a Payment is still included. +* New: Border styling rules have been added so autofill fields in Chrome and Safari will appear more consistent with other fields. +* New: A new frm_after_import_forms action has been added for handling an imported XML after all forms have been imported. +* Fix: A form would incorrectly try to submit a second time when redirecting and submitting with AJAX. + = 6.30 = * New: Product, Quantity, and Total fields are now available in Lite! These are all included in the Pricing Fields section, now located above the Advanced Fields section. * New: Global custom CSS is now scoped to only style form previews on admin pages to prevent custom CSS from changing the appearance of other elements on the page. diff --git a/classes/controllers/FrmAddonsController.php b/classes/controllers/FrmAddonsController.php index 74dde99a27..5308a19dd8 100644 --- a/classes/controllers/FrmAddonsController.php +++ b/classes/controllers/FrmAddonsController.php @@ -474,7 +474,7 @@ protected static function get_pro_from_addons( $addons ) { /** * @since 4.06 - * @since x.x Added the $force_type param. + * @since 6.31 Added the $force_type param. * * @param bool $force_type Whether to resolve grandfathered licenses to their real license type. * @@ -493,7 +493,7 @@ public static function license_type( $force_type = false ) { * * Mirrors the API's determine_status_from_license_details logic. * - * @since x.x + * @since 6.31 * * @return string 'active', 'expired', or 'free'. */ diff --git a/classes/controllers/FrmAppController.php b/classes/controllers/FrmAppController.php index d2b0d7bced..5d87c178e4 100644 --- a/classes/controllers/FrmAppController.php +++ b/classes/controllers/FrmAppController.php @@ -940,7 +940,7 @@ public static function admin_enqueue_scripts() { /** * Enqueue legacy PayPal action settings styles when the PayPal add-on is active. * - * @since x.x + * @since 6.31 * * @return void */ diff --git a/classes/controllers/FrmFormActionsController.php b/classes/controllers/FrmFormActionsController.php index 0709b0a652..862201f9b2 100644 --- a/classes/controllers/FrmFormActionsController.php +++ b/classes/controllers/FrmFormActionsController.php @@ -104,7 +104,7 @@ public static function register_actions() { * * Keeps the description when an add-on replaces a base action class without its own. * - * @since x.x + * @since 6.31 * * @return void */ @@ -304,7 +304,7 @@ public static function form_action_groups() { /** * Get the actions that are currently available (active) for My Actions section. * - * @since x.x + * @since 6.31 * * @param array $action_controls The registered action controls. * @@ -333,7 +333,7 @@ private static function get_available_my_actions( $action_controls ) { /** * Get the actions to include in the Misc section. * - * @since x.x + * @since 6.31 * * @param array $action_controls The registered action controls. * @@ -480,7 +480,7 @@ public static function show_action_icon_link( $action_control, $allowed ) { /** * Get the HTML attributes for the action icon. * - * @since x.x + * @since 6.31 * * @param object $action_control * @@ -669,7 +669,7 @@ public static function add_form_action() { /** * Returns the first available title not in $existing_titles, appending " (2)", " (3)", etc. if needed. * - * @since x.x + * @since 6.31 * * @param string $base_title Default action title from the action type. * @param string[] $existing_titles Titles currently visible in the form editor. @@ -1024,7 +1024,7 @@ public static function prevent_wpml_translations( $null, $post_type ) { * If Pro is not connected, hook a filter that will force all non-Lite * actions to inactive so the upgrade popup is shown instead. * - * @since x.x + * @since 6.31 * * @return void */ @@ -1044,7 +1044,7 @@ private static function maybe_setup_unlicensed_action_gate() { * so the per-key option filters are in place before the class constructors * run in the foreach loop that follows. * - * @since x.x + * @since 6.31 * * @param array $actions Map of action_key => class_name. * @@ -1078,7 +1078,7 @@ function ( $options ) { /** * Get action keys that are available in Lite without a Pro license. * - * @since x.x + * @since 6.31 * * @return string[] */ @@ -1093,7 +1093,7 @@ public static function get_lite_actions() { * Slugs without '/' are KB doc slugs (knowledgebase/ prefix is added). * Slugs with '/' are direct paths (e.g. features/) used as-is. * - * @since x.x + * @since 6.31 * * @return array Map of action_key => URL slug. */ @@ -1124,7 +1124,7 @@ public static function get_action_learn_more_links() { /** * Look up the learn-more doc slug for a given action key. * - * @since x.x + * @since 6.31 * * @param string $action_key Action identifier (e.g. 'register'). * diff --git a/classes/helpers/FrmAppHelper.php b/classes/helpers/FrmAppHelper.php index c977d11cb5..feb27b93f9 100644 --- a/classes/helpers/FrmAppHelper.php +++ b/classes/helpers/FrmAppHelper.php @@ -29,7 +29,7 @@ class FrmAppHelper { * * @var string */ - public static $plug_version = '6.30'; + public static $plug_version = '6.31'; /** * @var bool diff --git a/classes/helpers/FrmXMLHelper.php b/classes/helpers/FrmXMLHelper.php index 238d3aef79..fee315aa3c 100644 --- a/classes/helpers/FrmXMLHelper.php +++ b/classes/helpers/FrmXMLHelper.php @@ -303,7 +303,7 @@ public static function import_xml_forms( $forms, $imported ) { * imported later. Use this action for cross-form fix-ups that require * the fully populated `$frm_duplicate_ids` map. * - * @since x.x + * @since 6.31 * * @param array $imported Summary of imported items, including * 'forms' => array( old_form_id => new_form_id ). diff --git a/classes/models/FrmFormAction.php b/classes/models/FrmFormAction.php index 2e9da5b1a9..6a75dc51de 100644 --- a/classes/models/FrmFormAction.php +++ b/classes/models/FrmFormAction.php @@ -144,7 +144,7 @@ public function __construct( $id_base, $name, $action_options = array(), $contro $this->id_base = strtolower( $id_base ); /** - * @since x.x + * @since 6.31 * * @param string $name */ diff --git a/classes/models/fields/FrmFieldType.php b/classes/models/fields/FrmFieldType.php index 6f0d7eb0db..d310dca6aa 100644 --- a/classes/models/fields/FrmFieldType.php +++ b/classes/models/fields/FrmFieldType.php @@ -972,7 +972,7 @@ public function get_options( $values ) { */ public function show_field( $args ) { /** - * @since x.x Added $field parameter. + * @since 6.31 Added $field parameter. * * @param bool $show_normal_field_type * @param string $type diff --git a/classes/views/frm-fields/back-end/upsell/ai-upsell-button.php b/classes/views/frm-fields/back-end/upsell/ai-upsell-button.php index 2c7edf9752..a9d8b0bd86 100644 --- a/classes/views/frm-fields/back-end/upsell/ai-upsell-button.php +++ b/classes/views/frm-fields/back-end/upsell/ai-upsell-button.php @@ -4,7 +4,7 @@ * * @package Formidable * - * @since x.x + * @since 6.31 */ if ( ! defined( 'ABSPATH' ) ) { diff --git a/classes/views/frm-forms/add_field_links.php b/classes/views/frm-forms/add_field_links.php index e755e576c9..c4e3775529 100644 --- a/classes/views/frm-forms/add_field_links.php +++ b/classes/views/frm-forms/add_field_links.php @@ -231,7 +231,7 @@ * Used along with frm_extra_form_instruction_tabs which is used to * include the tabs. * - * @since x.x + * @since 6.31 * * @param stdClass $form The form object. */ diff --git a/formidable.php b/formidable.php index 1ea36e2c60..3e51962d6e 100644 --- a/formidable.php +++ b/formidable.php @@ -2,7 +2,7 @@ /** * Plugin Name: Formidable Forms * Description: Quickly and easily create drag-and-drop forms - * Version: 6.30 + * Version: 6.31 * Plugin URI: https://formidableforms.com/ * Author URI: https://formidableforms.com/ * Author: Strategy11 Form Builder Team diff --git a/js/formidable.min.js b/js/formidable.min.js index ea100e3af5..af5b879356 100644 --- a/js/formidable.min.js +++ b/js/formidable.min.js @@ -24,13 +24,13 @@ if(!recaptcha)return errors;const recaptchaID=recaptcha.dataset.rid;let response field.getAttribute(messageType);if(null===msg)msg="";if(""!==msg&&shouldWrapErrorHtmlAroundMessageType(messageType))msg=wrapErrorHtml(msg,field);return msg}function wrapErrorHtml(msg,field){let errorHtml=field.getAttribute("data-error-html");if(null===errorHtml)return msg;errorHtml=errorHtml.replace(/\+/g,"%20");msg=decodeURIComponent(errorHtml).replace("[error]",msg);const fieldId=getFieldId(field,false);const split=fieldId.split("-");const fieldIdParts=field.id.split("_");fieldIdParts.shift();split[0]= fieldIdParts.join("_");const errorKey=split.join("-");return msg.replace("[key]",errorKey)}function shouldWrapErrorHtmlAroundMessageType(type){return"pattern"!==type}function shouldJSValidate(object){if("function"===typeof object.get)object=object.get(0);let validate=hasClass(object,"frm_js_validate");if(validate&&typeof frmProForm!=="undefined"&&(frmProForm.savingDraft(object)||frmProForm.goingToPreviousPage(object)))validate=false;return validate}function getFormErrors(object,action){const fieldsets= object.querySelectorAll(".frm_form_field");fieldsets.forEach(field=>field.classList.add("frm_doing_ajax"));const data=`${jQuery(object).serialize()}&action=frm_entries_${action}&nonce=${frm_js.nonce}`;const shouldTriggerEvent=object.classList.contains("frm_trigger_event_on_submit");const doRedirect=response=>{jQuery(document).trigger("frmBeforeFormRedirect",[object,response]);if(!response.openInNewTab){window.location=response.redirect;return}const newTab=window.open(response.redirect,"_blank");if(!newTab&& -response.fallbackMsg&&response.content)response.content=response.content.trim().replace(/(<\/div><\/div>)$/,` ${response.fallbackMsg}`)};const success=function(response){const defaultResponse={content:"",errors:{},pass:false};if(response===null)response=defaultResponse;else{response=response.replace(/^\s+|\s+$/g,"");if(response.indexOf("{")===0)response=JSON.parse(response);else response=defaultResponse}if(response.redirect!==undefined){if(shouldTriggerEvent){triggerCustomEvent(object, -"frmSubmitEvent");return}if(response.delay)setTimeout(function(){doRedirect(response)},1E3*response.delay);else doRedirect(response)}if("string"===typeof response.content&&response.content!==""){if(shouldTriggerEvent){triggerCustomEvent(object,"frmSubmitEvent",{content:response.content});return}removeSubmitLoading(jQuery(object));if(frm_js.offset!=-1)frmFrontForm.scrollMsg(jQuery(object),false);const formIdInput=object.querySelector('input[name="form_id"]');const formID=formIdInput?formIdInput.value: -"";response.content=response.content.replace(/ frm_pro_form /g," frm_pro_form frm_no_hide ");const replaceContent=jQuery(object).closest(".frm_forms");removeAddedScripts(replaceContent,formID);const delay=maybeSlideOut(replaceContent,response.content);setTimeout(function(){afterFormSubmittedBeforeReplace(object,response);replaceContent.replaceWith(response.content);addUrlParam(response);if(typeof frmThemeOverride_frmAfterSubmit==="function"){const pageOrderInput=document.querySelector(`input[name="frm_page_order_${formID}"]`); -const pageOrder=pageOrderInput?pageOrderInput.value:"";const tempDiv=document.createElement("div");tempDiv.innerHTML=response.content;const formReturnedInput=tempDiv.querySelector('input[name="form_id"]');const formReturned=formReturnedInput?formReturnedInput.value:"";frmThemeOverride_frmAfterSubmit(formReturned,pageOrder,response.content,object)}afterFormSubmitted(object,response)},delay)}else if(Object.keys(response.errors).length){removeSubmitLoading(jQuery(object),"enable");let contSubmit=true; -removeAllErrors();let $fieldCont=null;for(const key in response.errors){const fieldContEl=object.querySelector(`#frm_field_${key}_container`);$fieldCont=fieldContEl?jQuery(fieldContEl):jQuery();if($fieldCont.length){if(!$fieldCont.is(":visible")){const inCollapsedSection=$fieldCont.closest(".frm_toggle_container");if(inCollapsedSection.length){let frmTrigger=inCollapsedSection.prev();if(!frmTrigger.hasClass("frm_trigger"))frmTrigger=frmTrigger.prev(".frm_trigger");frmTrigger.trigger("click")}}if($fieldCont.is(":visible")){addFieldError($fieldCont, +response.fallbackMsg&&response.content)response.content=response.content.trim().replace(/(<\/div><\/div>)$/,` ${response.fallbackMsg}`)};const success=function(response){const defaultResponse={content:"",errors:{},pass:false};if(response===null)response=defaultResponse;else{response=response.replace(/^\s+|\s+$/g,"");if(response.indexOf("{")===0)response=JSON.parse(response);else response=defaultResponse}let willRedirect=false;if(response.redirect!==undefined){if(shouldTriggerEvent){triggerCustomEvent(object, +"frmSubmitEvent");return}if(response.delay)setTimeout(function(){doRedirect(response)},1E3*response.delay);else doRedirect(response);willRedirect=true}if("string"===typeof response.content&&response.content!==""){if(shouldTriggerEvent){triggerCustomEvent(object,"frmSubmitEvent",{content:response.content});return}removeSubmitLoading(jQuery(object));if(frm_js.offset!=-1)frmFrontForm.scrollMsg(jQuery(object),false);const formIdInput=object.querySelector('input[name="form_id"]');const formID=formIdInput? +formIdInput.value:"";response.content=response.content.replace(/ frm_pro_form /g," frm_pro_form frm_no_hide ");const replaceContent=jQuery(object).closest(".frm_forms");removeAddedScripts(replaceContent,formID);const delay=maybeSlideOut(replaceContent,response.content);setTimeout(function(){afterFormSubmittedBeforeReplace(object,response);replaceContent.replaceWith(response.content);addUrlParam(response);if(typeof frmThemeOverride_frmAfterSubmit==="function"){const pageOrderInput=document.querySelector(`input[name="frm_page_order_${formID}"]`); +const pageOrder=pageOrderInput?pageOrderInput.value:"";const tempDiv=document.createElement("div");tempDiv.innerHTML=response.content;const formReturnedInput=tempDiv.querySelector('input[name="form_id"]');const formReturned=formReturnedInput?formReturnedInput.value:"";frmThemeOverride_frmAfterSubmit(formReturned,pageOrder,response.content,object)}afterFormSubmitted(object,response)},delay)}else if(response.errors!==undefined&&Object.keys(response.errors).length){removeSubmitLoading(jQuery(object), +"enable");let contSubmit=true;removeAllErrors();let $fieldCont=null;for(const key in response.errors){const fieldContEl=object.querySelector(`#frm_field_${key}_container`);$fieldCont=fieldContEl?jQuery(fieldContEl):jQuery();if($fieldCont.length){if(!$fieldCont.is(":visible")){const inCollapsedSection=$fieldCont.closest(".frm_toggle_container");if(inCollapsedSection.length){let frmTrigger=inCollapsedSection.prev();if(!frmTrigger.hasClass("frm_trigger"))frmTrigger=frmTrigger.prev(".frm_trigger");frmTrigger.trigger("click")}}if($fieldCont.is(":visible")){addFieldError($fieldCont, key,response.errors);contSubmit=false}}}object.querySelectorAll(".frm-g-recaptcha, .g-recaptcha, .h-captcha").forEach(function(captchaEl){const recaptchaID=captchaEl.dataset.rid;if(typeof grecaptcha!=="undefined"&&grecaptcha)if(recaptchaID)grecaptcha.reset(recaptchaID);else grecaptcha.reset();if(typeof hcaptcha!=="undefined"&&hcaptcha)hcaptcha.reset()});if(window.turnstile)object.querySelectorAll(".frm-cf-turnstile").forEach(turnstileField=>turnstileField.dataset.rid&&turnstile.reset(turnstileField.dataset.rid)); -jQuery(document).trigger("frmFormErrors",[object,response]);fieldsets.forEach(field=>field.classList.remove("frm_doing_ajax"));scrollToFirstField(object);if(contSubmit)object.submit();else{object.insertAdjacentHTML("afterbegin",response.error_message);checkForErrorsAndMaybeSetFocus()}}else{showFileLoading(object);object.submit()}};const error=function(){object.querySelectorAll('input[type="submit"], input[type="button"]').forEach(button=>button.disabled=false);object.submit()};postToAjaxUrl(object, +jQuery(document).trigger("frmFormErrors",[object,response]);fieldsets.forEach(field=>field.classList.remove("frm_doing_ajax"));scrollToFirstField(object);if(contSubmit)object.submit();else{object.insertAdjacentHTML("afterbegin",response.error_message);checkForErrorsAndMaybeSetFocus()}}else if(!willRedirect){showFileLoading(object);object.submit()}};const error=function(){object.querySelectorAll('input[type="submit"], input[type="button"]').forEach(button=>button.disabled=false);object.submit()};postToAjaxUrl(object, data,success,error)}function postToAjaxUrl(form,data,success,error){let ajaxUrl=frm_js.ajax_url;const action=form.getAttribute("action");if("string"===typeof action&&action.includes("?action=frm_forms_preview"))ajaxUrl=action.split("?action=frm_forms_preview")[0];const ajaxParams={type:"POST",url:ajaxUrl,data,success};if("function"===typeof error)ajaxParams.error=error;jQuery.ajax(ajaxParams)}function afterFormSubmitted(object,response){const tempDiv=document.createElement("div");tempDiv.innerHTML= response.content;const formCompleted=tempDiv.querySelector(".frm_message");if(formCompleted)jQuery(document).trigger("frmFormComplete",[object,response]);else jQuery(document).trigger("frmPageChanged",[object,response])}function afterFormSubmittedBeforeReplace(object,response){const tempDiv=document.createElement("div");tempDiv.innerHTML=response.content;const formCompleted=tempDiv.querySelector(".frm_message");if(formCompleted)triggerCustomEvent(document,"frmFormCompleteBeforeReplace",{object,response})} function removeAddedScripts(formContainer,formID){const endReplace=document.querySelectorAll(`.frm_end_ajax_${formID}`);if(endReplace.length){formContainer.nextUntil(`.frm_end_ajax_${formID}`).remove();endReplace.forEach(el=>el.remove())}}function maybeSlideOut(oldContent,newContent){let c;let newClass="frm_slideout";if(newContent.includes(" frm_slide")){c=oldContent.children();if(newContent.includes(" frm_going_back"))newClass+=" frm_going_back";c.removeClass("frm_going_back");c.addClass(newClass); @@ -58,16 +58,16 @@ end;return}const startTime=performance.now();const step=currentTime=>{const prog if(label&&captchaResponse)label.htmlFor=captchaResponse.id}function checkQuantityFieldMinMax(input){if(""===input.value)return 0;const val=parseFloat(input.value?input.value.trim():0);if(isNaN(val))return 0;let max=input.hasAttribute("max")?parseFloat(input.getAttribute("max")):0;let min=input.hasAttribute("min")?parseFloat(input.getAttribute("min")):0;max=isNaN(max)?0:max;min=isNaN(min)?0:Math.max(0,min);if(valmax){input.value=max;return max}return val} function triggerChange(input,fieldKey){if(fieldKey===undefined)fieldKey="dependent";jQuery(input).trigger({type:"change",selfTriggered:true,frmTriggered:fieldKey})}function calcProductsTotal(e){if("object"===typeof frmProForm)return;if(typeof __FRMCURR==="undefined")return;const totalFields=document.querySelectorAll("[data-frmtotal]");if(!totalFields.length)return;const formTotals=[];totalFields.forEach(totalField=>{let total=0;const form=totalField.closest("form");if(!form)return;const formId=form.querySelector('input[name="form_id"]').value; const currency=getCurrency(formId);if(undefined!==formTotals[formId])total=formTotals[formId];else{form.querySelectorAll("input[data-frmprice],select:has([data-frmprice])").forEach(function(input){let quantity=0;let price=0;const isSingle="hidden"===input.type;if(input.tagName==="SELECT"){if(input.selectedIndex!==-1)price=input.options[input.selectedIndex].getAttribute("data-frmprice")}else{if(!isSingle&&!input.matches(":checked"))return;price=input.getAttribute("data-frmprice")}if(!price)price=0; -else{price=preparePrice(price,currency);quantity=getQuantity(input);price=parseFloat(quantity)*parseFloat(price)}if("true"===input.getAttribute("data-frmdiscount"))price=price*-1;total+=price});formTotals[formId]=total}total=isNaN(total)?0:total;currency.decimal_separator=currency.decimal_separator.trim();if(!currency.decimal_separator.length)currency.decimal_separator=".";totalField.value=total;total=normalizeTotal(total,currency);triggerChange(totalField);total=formatCurrency(total,currency);const formatted= -totalField.previousElementSibling;if(formatted?.matches(".frm_total_formatted")){formatted.innerHTML=total;return}const formattedEls=totalField.closest(".frm_form_field").querySelectorAll(".frm_total_formatted");formattedEls.forEach(formattedEl=>{formattedEl.innerHTML=total})})}function normalizeTotal(total,currency){const isLargeTotal=total>Number.MAX_SAFE_INTEGER;if(!isLargeTotal){const {decimals}=currency;total=decimals>0?round10(total,decimals):Math.ceil(total)}return maybeAddTrailingZeroToPrice(total, -currency,isLargeTotal)}function round10(value,decimals){return Number(`${Math.round(`${value}e${decimals}`)}e-${decimals}`)}function formatCurrency(total,currency){total=maybeAddTrailingZeroToPrice(total,currency);if(total.length&&(total[total.length-1]==="."||total[total.length-1]===currency.decimal_separator))total=total.substr(0,total.length-1);total=maybeRemoveTrailingZerosFromPrice(total,currency);total=addThousands(total,currency);const leftSymbol=currency.symbol_left?currency.symbol_left+currency.symbol_padding: -"";const rightSymbol=currency.symbol_right?currency.symbol_padding+currency.symbol_right:"";return`${leftSymbol}${total}${rightSymbol}`}function getCurrency(formId){if(undefined!==window.__FRMCURR&&undefined!==window.__FRMCURR[formId])return window.__FRMCURR[formId];return{symbol_left:"$",symbol_right:"",symbol_padding:"",thousand_separator:",",decimal_separator:".",decimals:2}}function getQuantity(field){const fieldID=frmFrontForm.getFieldId(field,false);if(!fieldID)return 0;const quantityField= -getQuantityField(field,fieldID);if(!quantityField)return 1;return checkQuantityFieldMinMax(quantityField)}function getQuantityField(element,fieldID){const quantityFields=element.closest("form").querySelectorAll("[data-frmproduct]");if(!quantityFields.length)return null;fieldID=fieldID.toString();return Array.from(quantityFields).find(element=>{let ids;ids=JSON.parse(element.getAttribute("data-frmproduct").trim());if(""===ids)return false;ids="string"===typeof ids?[ids]:ids;return ids.includes(fieldID)})} -function preparePrice(price,currency){if(!price)return 0;price=`${price}`;const regex=getRegexForPrice(currency);const matches=price.match(regex);if(null===matches)return 0;price=matches.length?matches[matches.length-1]:0;price=price.trim();if(currency.decimal_separator==="."&&3===price.split(".").length&&price[0]===".")price=price.substr(1);if(price){price=maybeUseDecimal(price,currency);price=price.split(currency.thousand_separator).join("").replace(currency.decimal_separator,".")}return price} -function getRegexForPrice(currency){let regexString="[0-9,.";if(currency.thousand_separator!=="."&¤cy.thousand_separator!==",")regexString+=currency.thousand_separator;if(currency.decimal_separator!=="."&¤cy.decimal_separator!==",")regexString+=currency.decimal_separator;regexString+="]*\\.?\\,?[0-9]+";return new RegExp(regexString,"g")}function maybeUseDecimal(price,currency){let usedForDecimal;let priceParts;if("."===currency.thousand_separator){priceParts=price.split(".");usedForDecimal= -2===priceParts.length&&2===priceParts[1].length;if(usedForDecimal)price=price.replace(".",currency.decimal_separator)}return price}function maybeAddTrailingZeroToPrice(price,currency,force=false){if("number"!==typeof price&&!force)return price;price=String(price);const pos=price.indexOf(".");if(pos===-1){price=`${price}.`;for(let n=0;n{formattedEl.innerHTML=total})})}function normalizeTotal(total,currency){const isLargeTotal=total>Number.MAX_SAFE_INTEGER;total=roundTotal(total,currency);return maybeAddTrailingZeroToPrice(total,currency,isLargeTotal)}function roundTotal(total, +currency){const isLargeTotal=total>Number.MAX_SAFE_INTEGER;if(!isLargeTotal){const {decimals}=currency;total=decimals>0?round10(total,decimals):Math.ceil(total)}return total}function round10(value,decimals){return Number(`${Math.round(`${value}e${decimals}`)}e-${decimals}`)}function formatCurrency(total,currency){total=maybeAddTrailingZeroToPrice(total,currency);if(total.length&&(total[total.length-1]==="."||total[total.length-1]===currency.decimal_separator))total=total.substr(0,total.length-1); +total=maybeRemoveTrailingZerosFromPrice(total,currency);total=addThousands(total,currency);const leftSymbol=currency.symbol_left?currency.symbol_left+currency.symbol_padding:"";const rightSymbol=currency.symbol_right?currency.symbol_padding+currency.symbol_right:"";return`${leftSymbol}${total}${rightSymbol}`}function getCurrency(formId){if(undefined!==window.__FRMCURR&&undefined!==window.__FRMCURR[formId])return window.__FRMCURR[formId];return{symbol_left:"$",symbol_right:"",symbol_padding:"",thousand_separator:",", +decimal_separator:".",decimals:2}}function getQuantity(field){const fieldID=frmFrontForm.getFieldId(field,false);if(!fieldID)return 0;const quantityField=getQuantityField(field,fieldID);if(!quantityField)return 1;return checkQuantityFieldMinMax(quantityField)}function getQuantityField(element,fieldID){const quantityFields=element.closest("form").querySelectorAll("[data-frmproduct]");if(!quantityFields.length)return null;fieldID=fieldID.toString();return Array.from(quantityFields).find(element=>{let ids; +ids=JSON.parse(element.getAttribute("data-frmproduct").trim());if(""===ids)return false;ids="string"===typeof ids?[ids]:ids;return ids.includes(fieldID)})}function preparePrice(price,currency){if(!price)return 0;price=`${price}`;const regex=getRegexForPrice(currency);const matches=price.match(regex);if(null===matches)return 0;price=matches.length?matches[matches.length-1]:0;price=price.trim();if(currency.decimal_separator==="."&&3===price.split(".").length&&price[0]===".")price=price.substr(1);if(price){price= +maybeUseDecimal(price,currency);price=price.split(currency.thousand_separator).join("").replace(currency.decimal_separator,".")}return price}function getRegexForPrice(currency){let regexString="[0-9,.";if(currency.thousand_separator!=="."&¤cy.thousand_separator!==",")regexString+=currency.thousand_separator;if(currency.decimal_separator!=="."&¤cy.decimal_separator!==",")regexString+=currency.decimal_separator;regexString+="]*\\.?\\,?[0-9]+";return new RegExp(regexString,"g")}function maybeUseDecimal(price, +currency){let usedForDecimal;let priceParts;if("."===currency.thousand_separator){priceParts=price.split(".");usedForDecimal=2===priceParts.length&&2===priceParts[1].length;if(usedForDecimal)price=price.replace(".",currency.decimal_separator)}return price}function maybeAddTrailingZeroToPrice(price,currency,force=false){if("number"!==typeof price&&!force)return price;price=String(price);const pos=price.indexOf(".");if(pos===-1){price=`${price}.`;for(let n=0;n