1var BWFAN_Public; 2(function ($) { 3 "use strict"; 4 /* Serialize the form object */ 5 $.fn.bwfanac_serializeAndEncode = function () { 6 return $.map(this.serializeArray(), function (val) { 7 var field = $("input[name='" + val.name + "']"); 8 if (field.attr('type') == 'checkbox') { 9 if (field.prop("checked")) { 10 return [val.name, encodeURIComponent('1')].join('='); 11 } else { 12 return [val.name, encodeURIComponent('0')].join('='); 13 } 14 } else { 15 return [val.name, encodeURIComponent(val.value)].join('='); 16 } 17 } 18 ).join('&'); 19 }; 20 21 BWFAN_Public = { 22 23 checkout_form: $('form.checkout'), 24 last_edit_field: '', 25 current_step: '', 26 checkout_fields_data: {}, 27 last_posted_fields_data: {}, 28 capture_email_xhr: null, 29 checkout_fields: [], 30 is_block_checkout: false, 31 32 /** 33 * Translate a WC Blocks DOM id/name to the canonical billing_ / shipping_ key 34 * that insert_abandoned_cart() expects. 35 * 'billing-first-name' -> 'billing_first_name' 36 * 'email' -> 'billing_email' 37 */ 38 bwfan_normalize_field_name: function (raw) { 39 if (!raw || !BWFAN_Public.is_block_checkout) return raw; 40 if (raw === 'email' || raw === 'contact_email' || raw === 'contact-email') return 'billing_email'; 41 if (raw.indexOf('billing-') === 0) return 'billing_' + raw.substring(8).replace(/-/g, '_'); 42 if (raw.indexOf('shipping-') === 0) return 'shipping_' + raw.substring(9).replace(/-/g, '_'); 43 return raw; 44 }, 45 46 init: function () { 47 /** Block checkout shim: swap form scope + selectors when WC Checkout block is active. */ 48 this.is_block_checkout = !!(typeof bwfanParamspublic !== 'undefined' && parseInt(bwfanParamspublic._is_block_based_checkout)>0); 49 if (this.is_block_checkout) { 50 var $blockForm = $('div.wp-block-woocommerce-checkout, .wc-block-checkout'); 51 if ($blockForm.length) { 52 this.checkout_form = $blockForm; 53 } 54 } 55 this.checkout_fields = [ 56 'billing_first_name', 57 'billing_last_name', 58 'billing_company', 59 'billing_phone', 60 'billing_country', 61 'billing_address_1', 62 'billing_address_2', 63 'billing_city', 64 'billing_state', 65 'billing_postcode', 66 'shipping_first_name', 67 'shipping_last_name', 68 'shipping_company', 69 'shipping_country', 70 'shipping_address_1', 71 'shipping_address_2', 72 'shipping_city', 73 'shipping_state', 74 'shipping_postcode', 75 'shipping_phone', 76 'ship_to_different_address', 77 'shipping_same_as_billing', 78 'billing_same_as_shipping', 79 'payment_method', 80 'bwfan_birthday_date_mm', 81 'bwfan_birthday_date_dd', 82 'bwfan_birthday_date_yy', 83 ]; 84 85 if (bwfanParamspublic.bwfan_custom_checkout_field != undefined && bwfanParamspublic.bwfan_custom_checkout_field != null) { 86 this.checkout_fields = this.union_arrays(this.checkout_fields, bwfanParamspublic.bwfan_custom_checkout_field); 87 } 88 89 $.each(BWFAN_Public.checkout_fields, function (i, field_name) { 90 BWFAN_Public.checkout_fields_data[field_name] = ''; 91 }); 92 93 this.checkout_form.find('input,select').on('change', function () { 94 var id = $(this).attr('id'); 95 if ("undefined" !== id) { 96 BWFAN_Public.last_edit_field = id; 97 } 98 }); 99 100 101 },
102 bwfan_get_cookie: function (cname) { 103 var name = cname + "="; 104 var ca = document.cookie.split(';'); 105 for (var i = 0; i < ca.length; i++) { 106 var c = ca[i].trim(); 107 if (c.indexOf(name) !== 0) { 108 continue; 109 } 110 var c_val = c.substring(name.length, c.length); 111 try { 112 c_val = decodeURIComponent(c_val); 113 } catch (e) { 114 console.log("Error occurred while decoding (" + c + "). Error is: " + e.message); 115 } 116 return c_val; 117 } 118 return ""; 119 }, 120 bwfan_check_for_checkout_fields: function () { 121 if ($('.wfacp_page').length === 0) { 122 return; 123 } 124 var checkout_fields_data = bwfanParamspublic.bwfan_checkout_js_data; 125 if ('no' === checkout_fields_data) { 126 return; 127 } 128 var localstorage_data = window.localStorage.getItem('wfacp_form_values'); 129 if (null == localstorage_data) { 130 localstorage_data = ""; 131 } 132 var final_data = checkout_fields_data.fields; 133 134 if ('' !== localstorage_data) { 135 localstorage_data = JSON.parse(localstorage_data); 136 for (var key in localstorage_data) { 137 138 if (localstorage_data.hasOwnProperty(key)) { 139 final_data[key] = localstorage_data[key]; 140 } 141 } 142 143 } 144 if (final_data.hasOwnProperty('shipping_method')) { 145 delete final_data.shipping_method; 146 } 147 148 if (final_data.hasOwnProperty('ship_to_different_address')) { 149 $('#ship-to-different-address-checkbox').prop('checked', true).trigger('change'); 150 delete final_data.shipping_method; 151 } 152 153 window.localStorage.setItem('wfacp_form_values', JSON.stringify(final_data)); 154 populate_fields_value(); 155 }, 156 urlParam: function (name) { 157 var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href); 158 var value = 'undefined'; 159 160 if (null !== results) { 161 value = results[1]; 162 } 163 164 return value; 165 }, 166 bwfan_capture_email: function () { 167 var emailSel = BWFAN_Public.is_block_checkout ? '#billing_email, #email' : '#billing_email'; 168 $(document).on('focusout', emailSel, function () { 169 BWFAN_Public.bwfan_get_checkout_data(); 170 } 171 ); 172 173 var billing_email = jQuery('#billing_email').val(); 174 if ((!billing_email || billing_email === '') && BWFAN_Public.is_block_checkout) { 175 billing_email = jQuery('#email').val(); 176 } 177 if (billing_email && billing_email !== '') { 178 BWFAN_Public.bwfan_capture_data_on_page_load(); 179 BWFAN_Public.bwfan_process_email(billing_email); 180 } 181 }, 182 bwfan_get_checkout_data: function () { 183 var email = $('#billing_email').val(); 184 if ((!email || email === '') && BWFAN_Public.is_block_checkout) { 185 email = $('#email').val(); 186 } 187 if (email && email !== '') { 188 BWFAN_Public.bwfan_process_email(email); 189 } 190 }, 191 bwfan_capture_data_on_page_load: function () { 192 $.each(BWFAN_Public.checkout_fields, function (i, field_name) { 193 var $this = $('#' + field_name); 194 /** Block checkout uses kebab-case ids (billing-first-name) and #email for billing_email. */ 195 if ((!$this.length || !$this.val()) && BWFAN_Public.is_block_checkout) { 196 if (field_name === 'billing_email') { 197 $this = $('#email'); 198 } else if (field_name.indexOf('billing_') === 0) { 199 $this = $('#billing-' + field_name.substring(8).replace(/_/g, '-')); 200 } else if (field_name.indexOf('shipping_') === 0) { 201 $this = $('#shipping-' + field_name.substring(9).replace(/_/g, '-')); 202 } 203 } 204 BWFAN_Public.checkout_fields_data[field_name] = $this.val(); 205 }); 206 }, 207 bwfanUpdateOnAddressCheckbox: function ( field_name, field_value ) { 208 var checkout_formdata = BWFAN_Public.checkout_form.bwfanac_serializeAndEncode(); 209 checkout_formdata = bwfanac_deserialize_obj(checkout_formdata); 210 checkout_formdata[field_name] = field_value; 211 if ( 212 ! (( $('#ship-to-different-address-checkbox').length && $('#ship-to-different-address-checkbox').is(':checked') && !$('.wfacp_page').length ) || 213 $('#billing_same_as_shipping').length && $('#billing_same_as_shipping').is(':checked') || 214 $('#shipping_same_as_billing').length && $('#shipping_same_as_billing').is(':checked')) 215 ) { 216 for (var i = 0; i < BWFAN_Public.checkout_fields.length; i++) { 217 let field = BWFAN_Public.checkout_fields[i]; 218 var fields = ''; 219 220 if (checkout_formdata[field] && ($('#shipping_same_as_billing').length && !$('#shipping_same_as_billing').is(':checked')) || // For Aero Checkout - if shipping same as billing checkbox exists and is not checked 221 (!$('#ship-to-different-address-checkbox').is(':checked')) ) { 222 fields = field.replace('billing_', 'shipping_'); 223 } 224 if (checkout_formdata[field] && ( $('#billing_same_as_shipping').length && !$('#billing_same_as_shipping').is(':checked' ) )) { 225 fields = field.replace('shipping_', 'billing_'); 226 } 227 228 checkout_formdata[fields] = checkout_formdata[field]; 229 } 230 231 232 } 233 BWFAN_Public.checkout_fields_data = checkout_formdata; 234 }, 235 bwfan_captureCheckoutField: function () { 236 var field_name = $(this).attr('name') || $(this).attr('id'); 237 if (!field_name) { 238 return; 239 } 240 field_name = BWFAN_Public.bwfan_normalize_field_name(field_name); 241 242 var field_value = $(this).val(); 243 244 /** for checking checkbox fields **/ 245 if ($(this).attr('type') == 'checkbox') { 246 field_value = $(this).prop('checked') ? 1 : 0; 247 } 248 249 if (BWFAN_Public.checkout_fields.indexOf(field_name) === -1) { 250 return; 251 } 252 253 if (BWFAN_Public.checkout_fields_data[field_name] === field_value) { 254 return; 255 } 256 257 if ( ['ship_to_different_address', 'billing_same_as_shipping', 'shipping_same_as_billing'].includes( field_name ) ) { 258 setTimeout(function () { 259 BWFAN_Public.bwfanUpdateOnAddressCheckbox(field_name, field_value); 260 }, 200); 261 return; 262 } 263 BWFAN_Public.checkout_fields_data[field_name] = field_value; 264 265 // For WooCommerce - shipping same as billing 266 if ($('#ship-to-different-address-checkbox').length) { 267 BWFAN_Public.checkout_fields_data['ship_to_different_address']
267= $('#ship-to-different-address-checkbox').is(':checked') ? 1 : 0; 268 } 269 270 // For WC Blocks checkout - "use shipping address as billing address" (inverted semantics) 271 if (BWFAN_Public.is_block_checkout) { 272 var $blockSame = $('.wc-block-checkout__use-address-for-billing input[type=checkbox]'); 273 if ($blockSame.length) { 274 BWFAN_Public.checkout_fields_data['ship_to_different_address'] = $blockSame.is(':checked') ? 0 : 1; 275 } 276 } 277 278 // For AeroCheckout - billing same as shipping 279 if ($('#billing_same_as_shipping').length) { 280 BWFAN_Public.checkout_fields_data['billing_same_as_shipping'] = $('#billing_same_as_shipping').is(':checked') ? 1 : 0; 281 } 282 283 // For AeroCheckout - shipping same as billing 284 if ($('#shipping_same_as_billing').length) { 285 BWFAN_Public.checkout_fields_data['shipping_same_as_billing'] = $('#shipping_same_as_billing').is(':checked') ? 1 : 0; 286 } 287 288 var field = ''; 289 290 // For Aero Checkout - if billing same as shipping checkbox exists and is not checked 291 if ($('#billing_same_as_shipping').length && !$('#billing_same_as_shipping').is(':checked')) { 292 if (field_name.indexOf('shipping_') === 0) { 293 field = field_name.replace('shipping_', 'billing_'); 294 } 295 } 296 297 if ( 298 ($('#shipping_same_as_billing').length && !$('#shipping_same_as_billing').is(':checked')) || // For Aero Checkout - if shipping same as billing checkbox exists and is not checked 299 (!$('#ship-to-different-address-checkbox').is(':checked')) // For WooCommerce native checkout 300 ) { 301 if (field_name.indexOf('billing_') === 0) { 302 field = field_name.replace('billing_', 'shipping_'); 303 } 304 } 305 306 // For Aero Checkout - if billing/shipping same as shipping/billing checkbox not exists 307 if(!$('#shipping_same_as_billing').length && !$('#billing_same_as_shipping').length && $('.wfacp_page').length) { 308 if (field_name.indexOf('billing_') === 0) { 309 field = field_name.replace('billing_', 'shipping_'); 310 } 311 if (field_name.indexOf('shipping_') === 0) { 312 field = field_name.replace('shipping_', 'billing_'); 313 } 314 } 315 316 BWFAN_Public.checkout_fields_data[field] = field_value; 317 }, 318 bwfan_process_email: async function (email) { 319 if ('undefined' === typeof email) { 320 return; 321 } 322 if (('0' == bwfanParamspublic.bwfan_ab_enable || bwfanParamspublic.bwfan_ab_enable == "")) { 323 return; 324 } 325 /** 326 * Removed abort as it was making a loop when bonanza is enabled 327 */ 328 if (null !== BWFAN_Public.capture_email_xhr) { 329 return; 330 } 331 if ('' === email) { 332 return; 333 } 334 if ($('#bwfan_gdpr_consent').length && 1 != $('#bwfan_gdpr_consent').val()) { 335 return; 336 } 337 338 var aero_id = ($('#wfacp_aero_checkout_id').length > 0) ? $('#wfacp_aero_checkout_id').attr('content') : ''; 339 var step = ''; 340 if (aero_id) { 341 step = (BWFAN_Public.current_step) ? BWFAN_Public.current_step : 'single_step'; 342 } 343 344 var timezone = ''; 345 346 if (typeof Intl === "object" || typeof Intl.DateTimeFormat() === "object") { 347 var resolved = Intl.DateTimeFormat().resolvedOptions(); 348 if (resolved.hasOwnProperty('timeZone')) { 349 timezone = resolved.timeZone; 350 } 351 } 352 353 var utm_source = this.bwfan_get_cookie('utm_source'); 354 var utm_term = this.bwfan_get_cookie('utm_term'); 355 var utm_campaign = this.bwfan_get_cookie('utm_campaign'); 356 var utm_medium = this.bwfan_get_cookie('utm_medium'); 357 var utm_content = this.bwfan_get_cookie('utm_content'); 358 if ('' != utm_source || '' != utm_term || '' != utm_campaign || '' != utm_medium || '' != utm_content) { 359 BWFAN_Public.checkout_fields_data['handle_utm_grabber'] = { 360 'utm_source': utm_source, 361 'utm_term': utm_term, 362 'utm_campaign': utm_campaign, 363 'utm_medium': utm_medium, 364 'utm_content': utm_content, 365 }; 366 } 367 368 var pushengage_token = await BWFAN_Public.getPushToken(); 369 370 /** 371 * Block checkout: scrape all currently-rendered block inputs into checkout_fields_data 372 * right before POST, so values land even if individual change/input events were missed 373 * (React-controlled inputs sometimes swallow them). 374 */ 375 if (BWFAN_Public.is_block_checkout) { 376 $('#email, #contact_email, [name="contact_email"], [id^
376="billing-"], [id^="shipping-"], textarea[id*="order-notes"]').each(function () { 377 var raw = $(this).attr('name') || $(this).attr('id'); 378 var key = BWFAN_Public.bwfan_normalize_field_name(raw); 379 if (!key) return; 380 var val = ($(this).attr('type') === 'checkbox') ? ($(this).prop('checked') ? 1 : 0) : $(this).val(); 381 if (val !== undefined && val !== null) { 382 BWFAN_Public.checkout_fields_data[key] = val; 383 } 384 }); 385 386 /** Capture currently-selected block payment method (covers missed change events). */ 387 var $payChecked = $('.wp-block-woocommerce-checkout-payment-block input[type=radio]:checked, .wc-block-components-payment-method input[type=radio]:checked').first(); 388 if ($payChecked.length && $payChecked.val()) { 389 BWFAN_Public.checkout_fields_data['payment_method'] = $payChecked.val(); 390 } 391 392 /** 393 * When "use shipping as billing" is checked (block's default), the billing form is 394 * not rendered. Mirror shipping_* into billing_* so the cart row stores a usable 395 * billing address for recovery emails / merge tags. 396 */ 397 var $sameAddr = $('.wc-block-checkout__use-address-for-billing input[type=checkbox]'); 398 var useSameAddr = ($sameAddr.length === 0) || $sameAddr.is(':checked'); 399 if (useSameAddr) { 400 /** 401 * Always overwrite billing_* with shipping_* â if the user previously 402 * unchecked the box, typed billing values, then re-checked, the stale 403 * billing values must not win. billing_email is independent of the toggle. 404 */ 405 var fields = BWFAN_Public.checkout_fields_data; 406 Object.keys(fields).forEach(function (k) { 407 if (k.indexOf('shipping_') === 0) { 408 var bk = 'billing_' + k.substring(9); 409 if (bk === 'billing_email') return; 410 fields[bk] = fields[k]; 411 } 412 }); 413 } 414 } 415 416 /** Snapshot the values this POST carries â the change-listener dedupe compares against posted state, not the live cache. */ 417 BWFAN_Public.last_posted_fields_data = $.extend(true, {}, BWFAN_Public.checkout_fields_data); 418 419 BWFAN_Public.capture_email_xhr = $.ajax({ 420 url: bwfanParamspublic.wc_ajax_url.toString().replace('%%endpoint%%', 'bwfan_insert_abandoned_cart'), 421 type: 'POST', 422 global: false, 423 data: { 424 'email': email, 425 'action': 'bwfan_insert_abandoned_cart', 426 'checkout_fields_data': BWFAN_Public.checkout_fields_data, 427 'last_edit_field': BWFAN_Public.last_edit_field, 428 'current_step': step, 429 'current_page_id': bwfanParamspublic.current_page_id, 430 'timezone': timezone, 431 'aerocheckout_page_id': aero_id, 432 'pushengage_token': pushengage_token, 433 '_wpnonce': bwfanParamspublic.ajax_nonce 434 }, 435 success: function (res) { 436 if (parseInt(res.id) > 0 && 0 === $('#bwfan_cart_id').length) { 437 var cartIdHtml = '<input type="hidden" id="bwfan_cart_id" name="bwfan_cart_id" value="' + res.id + '" />'; 438 var $anchor = $('#billing_email_field'); 439 if (!$anchor.length && BWFAN_Public.is_block_checkout) { 440 $anchor = $('div.wp-block-woocommerce-checkout, .wc-block-checkout').first(); 441 } 442 if ($anchor.length) { 443 $anchor.after(cartIdHtml); 444 } 445 console.log('Cart ID: ' + res.id + ' captured.'); 446 } 447 } 448 }).always(function () { 449 BWFAN_Public.capture_email_xhr = null; 450 }); 451 }, 452 453 union_arrays: function (x, y) { 454 var obj = {}; 455 for (var i = x.length - 1; i >= 0; --i) 456 obj[x[i]] = x[i]; 457 for (var i = y.length - 1; i >= 0; --i) 458 obj[y[i]] = y[i]; 459 var res = []; 460 for (var k in obj) { 461 if (obj.hasOwnProperty(k)) 462 res.push(obj[k]); 463 } 464 return res; 465 }, 466 467 getPushToken: async function () { 468 try { 469 const subscriberID = await window.PushEngage.getSubscriberId(); 470 return subscriberID; 471 } catch (error) { 472 473 } 474 return null; 475 }, 476 }; 477 478 /** 479 * Abandoned cart JS Function Initiate here 480 */ 481 BWFAN_Public.abandoned_cart = function () { 482 if ('0' != bwfanParamspublic.bwfan_ab_enable && bwfanParamspublic.bwfan_ab_enable != "") { 483 BWFAN_Public.bwfan_capture_email(); 484 } 485 }; 486 487 BWFAN_Public.get_selected_unsubscribe_lists = function () {
488 const enabled = $('#bwfan-unsubscribe-lists').length > 0; 489 if (!enabled) { 490 return []; 491 } 492 493 const lists = []; 494 $('.bwfan-unsubscribe-list-checkbox input').each(function () { 495 if ($(this).attr('id') === 'bwfan-list-unsubscribe-all') { 496 $(this).is(':checked') && lists.push('all'); 497 return true; 498 } 499 500 if (!$(this).is(':checked')) { 501 lists.push(parseInt($(this).val())); 502 } 503 }); 504 return lists; 505 }; 506 507 /** 508 * Unsubscribe event to unsubscribe user on a link click 509 */ 510 BWFAN_Public.unsubscribe_event = function () { 511 $('#bwfan_unsubscribe').on('click', function (event) { 512 event.preventDefault(); 513 var $this = $(this); 514 if ($this.hasClass('bwfan_loading')) { 515 return; 516 } 517 $this.addClass('bwfan_loading'); 518 519 var urlParams = new URLSearchParams(window.location.search); 520 521 var recipient = $('#bwfan_unsubscribe_recipient'); 522 recipient = (0 === recipient.length) ? urlParams.get('subscriber_recipient') : recipient.html(); 523 524 var automation_id = $('#bwfan_automation_id'); 525 automation_id = (0 === automation_id.length) ? urlParams.get('automation_id') : automation_id.val(); 526 527 var broadcast_id = $('#bwfan_broadcast_id'); 528 broadcast_id = (0 === broadcast_id.length) ? urlParams.get('broadcast_id') : broadcast_id.val(); 529 530 var form_feed_id = $('#bwfan_form_feed_id'); 531 form_feed_id = (0 === form_feed_id.length) ? urlParams.get('form_feed_id') : form_feed_id.val(); 532 533 var sid = $('#bwfan_sid'); 534 sid = (0 === sid.length) ? urlParams.get('sid') : sid.val(); 535 536 var tid = $('#bwfan_tid'); 537 tid = (0 === tid.length) ? urlParams.get('tid') : tid.val(); 538 539 var oneClick = $('#bwfan_one_click').val(); 540 541 var uid = $('#bwfan_form_uid_id'); 542 uid = (0 === uid.length) ? urlParams.get('bwfan_form_uid_id') : uid.val(); 543 544 var bwfan_nonce = $('#bwfan_unsubscribe_nonce'); 545 546 var oneClickArgs = urlParams.get('List-Unsubscribe'); 547 oneClickArgs = (oneClickArgs == 'One-Click') ? 1 : 0; 548 549 if (uid == undefined && recipient == undefined) { 550 if ($(".bwfan_response").length === 0) { 551 $this.after("<div class='bwfan_response'>" + bwfanParamspublic.message_no_contact + "</div>"); 552 } 553 $('.bwfan_response').fadeIn(); 554 $this.removeClass('bwfan_loading'); 555 setTimeout(function () { 556 $('.bwfan_response').fadeOut("slow"); 557 }, 2500); 558 return; 559 } 560 561 var lists = BWFAN_Public.get_selected_unsubscribe_lists(); 562 if (lists.includes('all')) { 563 $('.bwfan-unsubscribe-list-checkbox input').removeAttr('checked'); 564 $('#bwfan-list-unsubscribe-all').prop('checked', true); 565 } 566 lists = JSON.stringify(lists); 567 568 $.ajax({ 569 method: 'post', 570 dataType: 'json', 571 url: bwfanParamspublic.ajax_url, 572 data: $('#bwfan_unsubscribe_fields').serialize() + "&action=bwfan_unsubscribe_user" + "&recipient=" + recipient + "&sid=" + sid + "&tid=" + tid + "&unsubscribe_lists=" + lists + "&_nonce=" + bwfan_nonce.val() + "&one_click=" + oneClick + "&one_click_get=" + oneClickArgs, 573 success: function (result) { 574 // Add the redirect handling here 575 if (result.redirect && result.redirect_url) { 576 window.location.href = result.redirect_url; 577 return; 578 } 579 580 $this.removeClass('bwfan_loading'); 581 582 var response_generated = $this.parent().find('.bwfan_response').html(); 583 if ('undefined' === typeof response_generated) { 584 $this.after("<div class='bwfan_response'></div>"); 585 } 586 587 if (0 === result.success) { 588 $('.bwfan_response').addClass('bwfan_error'); 589 $('.bwfan_response').fadeIn().html(result.message); 590 591 if (typeof bwfan_unsubscribe_preference !== 'undefined' && bwfan_unsubscribe_preference == 'one_click') {
592 $('.bwfan-unsubscribe-lists').hide(); 593 $('a#bwfan_unsubscribe').hide(); 594 } else { 595 setTimeout(function () { 596 $('.bwfan_response').fadeOut("slow"); 597 }, 2500); 598 } 599 600 return; 601 } 602 603 $('.bwfan_response').addClass('bwfan_success'); 604 $('.bwfan_response').fadeIn().html(result.message); 605 if ('1' === $('#bwfan_one_click').val()) { 606 $('#bwfan-list-unsubscribe-all').prop('checked', true); 607 } 608 609 if (typeof bwfan_unsubscribe_preference !== 'undefined' && bwfan_unsubscribe_preference == 'one_click') { 610 $('.bwfan-unsubscribe-lists').hide(); 611 $('a#bwfan_unsubscribe').hide(); 612 } else { 613 /** Content is refresh after success */ 614 $('#bwfan-unsubscribe-lists').load( 615 location.href + ' #bwfan-unsubscribe-lists > *' 616 ); 617 setTimeout(function () { 618 $('.bwfan_response').fadeOut('slow'); 619 }, 2500); 620 } 621 } 622 }); 623 }); 624 }; 625 626 /** 627 * Selected profile tags 628 * */ 629 BWFAN_Public.get_profile_tags = function () { 630 const enabled = $('#bwfan-profile-tags').length > 0; 631 if (!enabled) { 632 return []; 633 } 634 635 var tags = []; 636 $('.bwfan-profile-tag-checkbox input:checked').each(function () { 637 tags.push($(this).val()); 638 }); 639 return tags; 640 }; 641 642 /** 643 * selected profile lists 644 * 645 * */ 646 BWFAN_Public.get_profile_lists = function () { 647 const enabled = $('#bwfan-profile-lists').length > 0; 648 if (!enabled) { 649 return []; 650 } 651 652 const lists = []; 653 $('.bwfan-profile-list-checkbox input:checked').each(function () { 654 lists.push(parseInt($(this).val())); 655 }); 656 657 return lists; 658 } 659 660 /** 661 * Selected profile fields 662 * 663 * */ 664 BWFAN_Public.get_profile_fields = function () { 665 const enabled = $('#bwfan-profile-fields').length > 0; 666 if (!enabled) { 667 return []; 668 } 669 670 const fields = []; 671 const dobParts = { day: '', month: '', year: '' }; 672 let hasDobField = false; 673 674 $('.bwfan-profile-field input, .bwfan-profile-field textarea, .bwfan-profile-field select').each(function () { 675 const fieldSlug = $(this).data('slug'); 676 const dobPart = $(this).data('dob-part'); 677 678 // Handle DOB parts separately 679 if (fieldSlug === 'dob' && dobPart) { 680 hasDobField = true; 681 dobParts[dobPart] = $(this).val(); 682 return; // Skip adding to fields array 683 } 684 685 if ($(this).is(':radio') || $(this).is(':checkbox')) { 686 if ($(this).is(':checked')) { 687 if (!fields[fieldSlug]) { 688 fields[fieldSlug] = []; 689 } 690 fields[fieldSlug].push($(this).val()); 691 } 692 } else if (fieldSlug === 'timezone') { 693 fields[fieldSlug] = $(this).val(); 694 } else { 695 fields[fieldSlug] = $(this).val(); 696 } 697 }); 698 699 for (const slug in fields) { 700 if (Array.isArray(fields[slug])) { 701 fields[slug] = fields[slug].join(', '); 702 } 703 } 704 705 const result = []; 706 707 // Add DOB parts if present 708 if (hasDobField) { 709 result.push({ slug: 'dob', value: dobParts.day, dob_part: 'day' }); 710 result.push({ slug: 'dob', value: dobParts.month, dob_part: 'month' }); 711 result.push({ slug: 'dob', value: dobParts.year, dob_part: 'year' }); 712 } 713 714 for (const slug in fields) { 715 const value = Array.isArray(fields[slug]) ? fields[slug].join(', ') : fields[slug]; 716 result.push({slug: slug, value: value}); 717 } 718 719 return result; 720 } 721 722 BWFAN_Public.update_profile = function () { 723 const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; 724 $('#bwfan_manage_profile_update').on('click', function (event) { 725 event.preventDefault(); 726 var $this = $(this); 727 728 // Clear previous DOB errors (errors are added as siblings with .after()) 729 $('.bwfan-dob-wrapper').next('.bwfan_error').remove(); 730 $('.bwfan-dob-field').removeClass('bwfan_error'); 731 732 // Validate DOB fields if present 733 const dobFields = $('.bwfan-dob-field[data-slug="dob"]'); 734 if (dobFields.length > 0) { 735 let dobDay = ''; 736 let dobMonth = ''; 737 let dobYear = ''; 738 let dobWrapper = null; 739 740 dobFields.each(function() { 741 const dobPart = $(this).data('dob-part'); 742 if (!dobWrapper) { 743 dobWrapper = $(this).closest('.bwfan-dob-wrapper'); 744 } 745 746 if (dobPart === 'day') dobDay = $(this).val(); 747 if (dobPart === 'month') dobMonth = $(this).val(); 748 if (dobPart === 'year') dobYear = $(this).val(); 749 }); 750 751 // Check if all parts are selected or all are empty 752 const hasAnyValue = dobDay || dobMonth || dobYear; 753 const hasAllValues = dobDay && dobMonth && dobYear; 754 const allEmpty = !dobDay && !dobMonth && !dobYear; 755 756 // If all empty, it's valid (optional field) 757 if (allEmpty) { 758 // DOB is optional, all empty is valid 759 } else if (hasAnyValue && !hasAllValues) { 760 // Some parts selected but not all 761 dobFields.addClass('bwfan_error'); 762 dobWrapper.after("<p class='bwfan_response bwfan_error'>"+bwfanParamspublic.dob_not_all_parts+"</p>"); 763 return; 764 } else if (hasAllValues) { 765 // If all values present, validate date is valid 766 const day = parseInt(dobDay, 10);
767 const month = parseInt(dobMonth, 10); 768 const year = parseInt(dobYear, 10); 769 770 // Check if date is valid 771 const date = new Date(year, month - 1, day); 772 if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day) { 773 dobFields.addClass('bwfan_error'); 774 dobWrapper.after("<p class='bwfan_response bwfan_error'>"+bwfanParamspublic.dob_not_valid+"</p>"); 775 return; 776 } 777 778 // Check if date is not in the future 779 const today = new Date(); 780 if (date > today) { 781 dobFields.addClass('bwfan_error'); 782 dobWrapper.after("<p class='bwfan_response bwfan_error'>"+bwfanParamspublic.dob_not_in_future+"</p>"); 783 return; 784 } 785 } 786 } 787 788 var emailField = $('input[data-slug="email"]'); 789 if( emailField.length > 0 ) { 790 if (emailField.val() === '') { 791 // Check if error message already exists 792 if (emailField.next('.bwfan_error').length === 0) { 793 $('#bwfan-field-email').addClass('bwfan_error'); 794 emailField.after("<p class='bwfan_response bwfan_error'>"+bwfanParamspublic.email_not_fill+"</p>"); 795 } else { 796 emailField.next('.bwfan_error').fadeIn().html( bwfanParamspublic.email_not_fill ); 797 } 798 return; // Stop the process if email is empty 799 } else if( ! emailRegex.test(emailField.val()) ) { 800 if (emailField.next('.bwfan_error').length === 0) { 801 $('#bwfan-field-email').addClass('bwfan_error'); 802 emailField.after("<p class='bwfan_response bwfan_error'>"+bwfanParamspublic.email_not_valid+"</p>"); 803 } else { 804 emailField.next('.bwfan_error').fadeIn().html(bwfanParamspublic.email_not_valid); 805 } 806 return; 807 } else{ 808 emailField.next('.bwfan_error').remove(); 809 $('#bwfan-field-email').removeClass('bwfan_error'); 810 } 811 } 812 813 var errorExists = $('#bwfan_manage_profile_fields' ).find('.bwfan_response.bwfan_error').length > 0; 814 if( errorExists ) { 815 // Get the first error element 816 var firstError = $('#bwfan_manage_profile_fields' ).find('.bwfan_response.bwfan_error')[0]; 817 setTimeout( () => { 818 var distractionMode = $( '.bwfan-manage-profile-wrapper.bwf-distraction-free-mode' ).length > 0; 819 if( distractionMode ) { 820 firstError.scrollIntoView({ 821 behavior: 'smooth', 822 block: 'start', 823 inline: 'nearest' 824 }); 825 return; 826 } 827 828 // Scroll to the position of the first error element 829 window.scrollTo({ 830 top: firstError.getBoundingClientRect().top + window.scrollY - 100, 831 behavior: 'smooth' 832 }); 833 }, 300 ); 834 } 835 836 if ($this.hasClass('bwfan_loading')) { 837 return; 838 } 839 840 841 var bwfan_nonce = $('#bwfan_manage_profile_nonce'); 842 843 var lists = BWFAN_Public.get_profile_lists(); 844 lists = JSON.stringify(lists); 845 846 var tags = BWFAN_Public.get_profile_tags(); 847 tags = JSON.stringify(tags); 848 849 var fields = BWFAN_Public.get_profile_fields(); 850 fields = JSON.stringify(fields); 851 852 var uid = $('#bwfan_form_uid_id'); 853 uid = (0 === uid.length) ? urlParams.get('bwfan_form_uid_id') : uid.val(); 854 855 $this.addClass('bwfan_loading'); 856 857 $.ajax({ 858 method: 'post', 859 dataType: 'json', 860 url: bwfanParamspublic.ajax_url, 861 data: $('#bwfan_manage_profile_update').serialize() + "&action=bwfan_manage_profile" + "&uid=" + uid + "&profile_lists=" + lists + "&profile_tags=" + tags + "&profile_fields="
861+ fields + "&_nonce=" + bwfan_nonce.val(), 862 success: function (result) { 863 $this.removeClass('bwfan_loading'); 864 var response_generated = $this.parent().find('.bwfan_response').html(); 865 if ('undefined' === typeof response_generated) { 866 $this.after("<div class='bwfan_response api-response'></div>"); 867 } 868 let message = result.hasOwnProperty( 'message' ) ? result.message : bwfanParamspublic.profile_success; 869 switch (parseInt(result.success)) { 870 case 10: 871 if (emailField.next('.bwfan_error').length === 0) { 872 $('#bwfan-field-email').addClass('bwfan_error'); 873 emailField.after(`<p class='bwfan_response bwfan_error'>${message}</p>`); 874 } 875 break; 876 case 0: 877 $('.bwfan_response.api-response').addClass('bwfan_error'); 878 $('.bwfan_response.api-response').fadeIn().html(message); 879 break; 880 default: 881 if ($('.bwfan_response.api-response').length > 0) { 882 $('.bwfan_response.api-response').removeClass('bwfan_error'); 883 } 884 $('.bwfan_response.api-response').addClass('bwfan_success'); 885 $('.bwfan_response.api-response').fadeIn().html(message); 886 break; 887 } 888 setTimeout(function () { 889 $('.bwfan_response.api-response').fadeOut("slow"); 890 }, 3000); 891 } 892 }); 893 }); 894 $(document).ready(function () { 895 // Datetime fields 896 const dateTimeFields = document.querySelectorAll('#bwfan-profile-fields .bwfan-profile-field input[type="text"][data-type="date-time"]'); 897 dateTimeFields.forEach(field => { 898 field.addEventListener('input', function () { 899 let value = $(this).val().trim().replace(/\D/g, ''); // Strip non-digits 900 let formatted = ''; 901 if (value.length > 0) formatted += value.substring(0, 4); // YYYY 902 if (value.length > 4) formatted += '-' + value.substring(4, 6); // MM 903 if (value.length > 6) formatted += '-' + value.substring(6, 8); // DD 904 if (value.length > 8) formatted += ' ' + value.substring(8, 10); // HH 905 if (value.length > 10) formatted += ':' + value.substring(10, 12); // mm 906 if (value.length > 12) formatted += ':' + value.substring(12, 14); // ss 907 908 $(this).val(formatted); 909 }); 910 911 field.addEventListener('blur', function () { 912 const val = $(this).val().trim(); 913 914 if( val == '' ) { 915 $(this).next('.bwfan_error').remove(); 916 $(this).removeClass('bwfan_error'); 917 return; 918 } 919 920 if (val.length !== 19) { 921 let error = 'Invalid date-time format. Please use YYYY-MM-DD HH:MM:SS'; 922 if ($(this).next('.bwfan_error').length === 0) { 923 $(this).addClass('bwfan_error'); 924 $(this).after("<p class='bwfan_response bwfan_error'>"+error+"</p>"); 925 } else { 926 $(this).next('.bwfan_error').fadeIn().html(error); 927 } 928 return; 929 } 930 931 const regex = /^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/; 932 const match = val.match(regex); 933 934 if (!match) { 935 let error = 'Invalid format. Please use YYYY-MM-DD HH:MM:SS'; 936 if ($(this).next('.bwfan_error').length === 0) { 937 $(this).addClass('bwfan_error'); 938 $(this).after("<p class='bwfan_response bwfan_error'>"+error+"</p>"); 939 } else { 940 $(this).next('.bwfan_error').fadeIn().html(error); 941 } 942 return; 943 } 944 945 const [_, year, month, day, hour, minute, second] = match.map(Number); 946 947 const isValidDate = (y, m, d, h, min, s) => { 948 if (m < 1 || m > 12) return false;
949 if (d < 1 || d > 31) return false; 950 if (h < 0 || h > 23) return false; 951 if (min < 0 || min > 59) return false; 952 if (s < 0 || s > 59) return false; 953 954 const date = new Date(y, m - 1, d); 955 return date.getFullYear() === y && 956 date.getMonth() === m - 1 && 957 date.getDate() === d; 958 }; 959 if (!isValidDate(year, month, day, hour, minute, second)) { 960 let error = 'Invalid date or time entered.'; 961 if ($(this).next('.bwfan_error').length === 0) { 962 $(this).addClass('bwfan_error'); 963 $(this).after("<p class='bwfan_response bwfan_error'>"+error+"</p>"); 964 } else { 965 $(this).next('.bwfan_error').fadeIn().html(error); 966 } 967 return; 968 } 969 $(this).next('.bwfan_error').remove(); 970 $(this).removeClass('bwfan_error'); 971 }); 972 }); 973 974 // Date field 975 const dateFields = document.querySelectorAll('#bwfan-profile-fields .bwfan-profile-field input[type="text"][data-type="date"]'); 976 dateFields.forEach(field => { 977 field.addEventListener('input', function () { 978 let value = $(this).val().trim().replace(/\D/g, ''); // Strip non-digits 979 let formatted = ''; 980 if (value.length > 0) formatted += value.substring(0, 4); // YYYY 981 if (value.length > 4) formatted += '-' + value.substring(4, 6); // MM 982 if (value.length > 6) formatted += '-' + value.substring(6, 8); // DD 983 984 $(this).val(formatted); 985 }); 986 987 field.addEventListener('blur', function () { 988 const val = $(this).val().trim(); 989 if( val == '' ) { 990 $(this).next('.bwfan_error').remove(); 991 $(this).removeClass('bwfan_error'); 992 return; 993 } 994 if (val.length !== 10) { 995 let error = 'Invalid date format. Please use YYYY-MM-DD'; 996 if ($(this).next('.bwfan_error').length === 0) { 997 $(this).addClass('bwfan_error'); 998 $(this).after("<p class='bwfan_response bwfan_error'>"+error+"</p>"); 999 } else { 1000 $(this).next('.bwfan_error').fadeIn().html(error); 1001 } 1002 return; 1003 } 1004 1005 const regex = /^(\d{4})-(\d{2})-(\d{2})$/; 1006 const match = val.match(regex); 1007 1008 if (!match) { 1009 let error = 'Invalid format. Please use YYYY-MM-DD'; 1010 if ($(this).next('.bwfan_error').length === 0) { 1011 $(this).addClass('bwfan_error'); 1012 $(this).after("<p class='bwfan_response bwfan_error'>"+error+"</p>"); 1013 } else { 1014 $(this).next('.bwfan_error').fadeIn().html(error); 1015 } 1016 return; 1017 } 1018 1019 const [_, year, month, day] = match.map(Number); 1020 1021 const isValidDate = (y, m, d) => { 1022 if (m < 1 || m > 12) return false; 1023 if (d < 1 || d > 31) return false; 1024 1025 const date = new Date(y, m - 1, d); 1026 return date.getFullYear() === y && 1027 date.getMonth() === m - 1 && 1028 date.getDate() === d; 1029 }; 1030 if (!isValidDate(year, month, day)) { 1031 let error = 'Invalid date entered.'; 1032 if ($(this).next('.bwfan_error').length === 0) { 1033 $(this).addClass('bwfan_error'); 1034 $(this).after("<p class='bwfan_response bwfan_error'>"+error+"</p>"); 1035 } else { 1036 $(this).next('.bwfan_error').fadeIn().html(error); 1037 } 1038 return; 1039 } 1040 $(this).next('.bwfan_error').remove(); 1041 $(this).removeClass('bwfan_error'); 1042 }); 1043 }); 1044 }); 1045 1046 // Time field 1047 const timeFields = document.querySelectorAll('#bwfan-profile-fields .bwfan-profile-field input[type="text"][data-type="time"]'); 1048 timeFields.forEach(field => { 1049 field.addEventListener('input', function () { 1050 let value = $(this).val().trim().replace(/\D/g, ''); // Strip non-digits 1051 let formatted = ''; 1052 if (value.length > 0) formatted += value.substring(0, 2); // HH 1053 if (value.length > 2) formatted += ':' + value.substring(2, 4); // MM 1054 if (value.length > 4) formatted += ':' + value.substring(4, 6); // SS 1055 1056 $(this).val(formatted); 1057 }); 1058 1059 field.addEventListener('blur', function () { 1060 const val = $(this).val().trim(); 1061 1062 if( val == '' ) { 1063 $(this).next('.bwfan_error').remove(); 1064 $(this).removeClass('bwfan_error'); 1065 return; 1066 } 1067 1068 if (val.length !== 8) { 1069 let error = 'Invalid time format. Please use H
1069H:MM:SS'; 1070 if ($(this).next('.bwfan_error').length === 0) { 1071 $(this).addClass('bwfan_error'); 1072 $(this).after("<p class='bwfan_response bwfan_error'>"+error+"</p>"); 1073 } else { 1074 $(this).next('.bwfan_error').fadeIn().html(error); 1075 } 1076 return; 1077 } 1078 1079 const regex = /^(\d{2}):(\d{2}):(\d{2})$/; 1080 const match = val.match(regex); 1081 1082 if (!match) { 1083 let error = 'Invalid format. Please use HH:MM:SS'; 1084 if ($(this).next('.bwfan_error').length === 0) { 1085 $(this).addClass('bwfan_error'); 1086 $(this).after("<p class='bwfan_response bwfan_error'>"+error+"</p>"); 1087 } else { 1088 $(this).next('.bwfan_error').fadeIn().html(error); 1089 } 1090 return; 1091 } 1092 1093 const [_, hour, minute, second] = match.map(Number); 1094 1095 const isValidDate = (h, min, s) => { 1096 if (h < 0 || h > 23) return false; 1097 if (min < 0 || min > 59) return false; 1098 if (s < 0 || s > 59) return false; 1099 1100 return true; 1101 }; 1102 if (!isValidDate(hour, minute, second)) { 1103 let error = 'Invalid time entered.'; 1104 if ($(this).next('.bwfan_error').length === 0) { 1105 $(this).addClass('bwfan_error'); 1106 $(this).after("<p class='bwfan_response bwfan_error'>"+error+"</p>"); 1107 } else { 1108 $(this).next('.bwfan_error').fadeIn().html(error); 1109 } 1110 return; 1111 } 1112 $(this).next('.bwfan_error').remove(); 1113 $(this).removeClass('bwfan_error'); 1114 }); 1115 }); 1116 } 1117 1118 /* Initialize */ 1119 BWFAN_Public.init(); 1120 1121 $(document).ready(function () { 1122 if ('0' != bwfanParamspublic.bwfan_ab_enable && bwfanParamspublic.bwfan_ab_enable != '') { 1123 var bwfan_email_consent_message = ''; 1124 if ('0' != bwfanParamspublic.bwfan_ab_email_consent && bwfanParamspublic.bwfan_ab_email_consent != '') { 1125 var site_language = bwfanParamspublic.bwfan_site_language; 1126 var message_index = 'bwfan_ab_email_consent_message_' + site_language; 1127 var label = bwfanParamspublic.hasOwnProperty(message_index) && bwfanParamspublic[message_index] != '' ? bwfanParamspublic[message_index] : bwfanParamspublic.bwfan_ab_email_consent_message; 1128 bwfan_email_consent_message = '<label>' + label + '</label>'; 1129 1130 // Remove/Strip slashes 1131 bwfan_email_consent_message = bwfan_email_consent_message.replace(new RegExp("\\\\", "g"), ""); 1132 var start = bwfan_email_consent_message.search("{{no_thanks label="); 1133 var end = bwfan_email_consent_message.search("}}"); 1134 if (-1 != start && -1 != end) { 1135 var temp_start = start + 19; 1136 var temp_end = end - 1; 1137 var no_thanks_merge_tag = bwfan_email_consent_message.substring(start, (end + 2)); 1138 var no_thanks_label = bwfan_email_consent_message.substring(temp_start, temp_end); 1139 no_thanks_label = no_thanks_label ? no_thanks_label : bwfanParamspublic.bwfan_no_thanks; 1140 bwfan_email_consent_message = bwfan_email_consent_message.replace(no_thanks_merge_tag, "<a class='bwfan_email_consent_no_thanks' style='text-decoration:underline;cursor: pointer;'>" + no_thanks_label + "</a>"); 1141 } else { 1142 bwfan_email_consent_message = bwfan_email_consent_message.replace("{{no_thanks}}", "<a class='bwfan_email_consent_no_thanks' style='text-decoration:underline;cursor: pointer;'>" + bwfanParamspublic.bwfan_no_thanks + "</a>"); 1143 } 1144 } 1145 1146 var emailConsentHtml = '<input type="hidden" id="bwfan_gdpr_consent" value="1" />'; 1147 if ('' === bwfan_email_consent_message) { 1148 $('#billing_email_field').after(emailConsentHtml); 1149 } else { 1150 bwfan_email_consent_message += emailConsentHtml; 1151 $('#billing_email_field').after('<p class="form-row form-row-wide wfacp-form-control-wrapper wfacp-col-full wfacp-anim-wrap">' + bwfan_email_consent_message + '</p>'); 1152 } 1153 1154 $('.bwfan_email_consent_no_thanks').on('click', function (event) { 1155 event.preventDefault(); 1156 1157 $.post(bwfanParamspublic.wc_ajax_url.toString().replace('%%endpoint%%', 'bwfan_delete_abandoned_cart'), { 1158 'email': $('#billing_email').val(), 1159 'action': 'bwfan_delete_abandoned_cart', 1160 '_wpnonce': bwfanParamspublic.ajax_nonce 1161 }, function () { 1162 // 1163 } 1164 ); 1165 $('#bwfan_gdpr_consent').val('0'); 1166 $(this).parent().fadeOut("slow"); 1167 }); 1168 } 1169 }); 1170 $(window).on('load', function () { 1171 BWFAN_Public.abandoned_cart(); 1172 BWFAN_Public.unsubscribe_event(); 1173 BWFAN_Public.update_profile(); 1174 BWFAN_Public.bwfan_check_for_checkout_fields(); 1175 BWFAN_Public.bwfan_capture_data_on_page_load(); 1176 $(document.body).on('wfacp_step_switching', function (e, v) { 1177 BWFAN_Public.current_step = v.current_step; 1178 var email = $('#billing_email').val(); 1179 if (email !== '') { 1180 BWFAN_Public.bwfan_process_email(email); 1181 } 1182 }); 1183 1184 /** 1185 * Detect change and save data in database 1186 */ 1187 BWFAN_Public.checkout_form.on('change', 'select', BWFAN_Public.bwfan_captureCheckoutField); 1188 BWFAN_Public.checkout_form.on('click', '.input-checkbox', BWFAN_Public.bwfan_captureCheckoutField); 1189 BWFAN_Public.checkout_form.on('change', '.input-checkbox', BWFAN_Public.bwfan_captureCheckoutField); 1190 BWFAN_Public.checkout_form.on('change', '.input-text', BWFAN_Public.bwfan_captureCheckoutField); 1191 1192 /**
1193 * WC Blocks checkout: inputs re-render on every Store API push (cart/shipping/totals), 1194 * so direct .on() bindings get detached. Delegate via document instead. 1195 * React-controlled text inputs fire 'input' reliably; 'change'/'blur' may be missed. 1196 */ 1197 if (BWFAN_Public.is_block_checkout) { 1198 var blockInputs = '#email, #contact_email, [name="contact_email"], [id^="billing-"], [id^="shipping-"], textarea[id*="order-notes"], .wc-block-checkout__use-address-for-billing input[type=checkbox]'; 1199 $(document).on('change input blur focusout', blockInputs, BWFAN_Public.bwfan_captureCheckoutField); 1200 1201 /** 1202 * "Use same address for billing" toggle has an auto-generated id (e.g. checkbox-control-1) 1203 * and no name attribute, so bwfan_captureCheckoutField can't classify it. 1204 * Wire a dedicated handler: translate state to ship_to_different_address and re-POST. 1205 */ 1206 var sameAddrSel = '.wc-block-checkout__use-address-for-billing input[type=checkbox]'; 1207 $(document).on('change', sameAddrSel, function () { 1208 var sameAddr = $(this).is(':checked'); 1209 BWFAN_Public.checkout_fields_data['ship_to_different_address'] = sameAddr ? 0 : 1; 1210 /** 1211 * When the user re-checks "use shipping as billing", the block hides the billing 1212 * form so any previously typed billing_* values become unreachable. Drop them so 1213 * the pre-POST mirror (shipping_* -> billing_*) is what lands on the cart row. 1214 */ 1215 if (sameAddr) { 1216 Object.keys(BWFAN_Public.checkout_fields_data).forEach(function (k) { 1217 if (k.indexOf('billing_') === 0 && k !== 'billing_email') { 1218 delete BWFAN_Public.checkout_fields_data[k]; 1219 } 1220 }); 1221 } else { 1222 Object.keys(BWFAN_Public.checkout_fields_data).forEach(function (k) { 1223 if (k.indexOf('billing_') === 0 && k !== 'billing_email') { 1224 BWFAN_Public.checkout_fields_data[k] = ''; 1225 } 1226 }); 1227 } 1228 if (typeof BWFAN_Public.bwfan_get_checkout_data === 'function') { 1229 setTimeout(BWFAN_Public.bwfan_get_checkout_data, 300); 1230 } 1231 }); 1232 1233 /** 1234 * Block payment method radios use auto-generated names (radio-control-X). The radio 1235 * value attribute carries the payment gateway slug (cod, bacs, stripe, ...). Map to 1236 * canonical `payment_method` key. 1237 */ 1238 var paySel = '.wp-block-woocommerce-checkout-payment-block input[type=radio], .wc-block-components-payment-method input[type=radio]'; 1239 $(document).on('change', paySel, function () { 1240 var val = $(this).val(); 1241 if (val) { 1242 BWFAN_Public.checkout_fields_data['payment_method'] = val; 1243 if (typeof BWFAN_Public.bwfan_get_checkout_data === 'function') { 1244 setTimeout(BWFAN_Public.bwfan_get_checkout_data, 300); 1245 } 1246 } 1247 }); 1248 } 1249 /** 1250 * Safety net for inputs whose `change` never fires (scripted fills, autofill). 1251 * 1252 * This previously called bwfan_captureCheckoutField() from an arrow function with no 1253 * element context, so `this` was BWFAN_Public and the call returned at its first guard â 1254 * it captured nothing. Read the field off the blurred element, then capture. 1255 */ 1256 BWFAN_Public.checkout_form.on('blur focusout', '.input-text', function () { 1257 BWFAN_Public.bwfan_captureCheckoutField.call(this); 1258 }); 1259 // Reset the 'triggered' flag when the input element gets focused again 1260 BWFAN_Public.checkout_form.on('focus', '.input-text', function () { 1261 $(this).removeData('triggered'); 1262 }); 1263 1264 var interval = null; 1265 var globalSel = '#billing_email,.input-text,.input-checkbox, select'; 1266 var globalEvents = 'blur change'; 1267 if (BWFAN_Public.is_block_checkout) { 1268 globalSel += ', #email, #contact_email, [name="contact_email"], [id^
1268="billing-"], [id^="shipping-"], textarea[id*="order-notes"]'; 1269 /** React-controlled inputs fire 'input' reliably; blur often missed via delegation. */ 1270 globalEvents = 'blur change input'; 1271 } 1272 $(document).on(globalEvents, globalSel, function () { 1273 var field_name = $(this).attr('name') || $(this).attr('id'); 1274 if (!field_name) { 1275 return; 1276 } 1277 field_name = BWFAN_Public.bwfan_normalize_field_name(field_name); 1278 1279 /** 1280 * Block checkout: billing_email isn't in checkout_fields but must still 1281 * trigger the POST, and React-controlled inputs can swallow discrete 1282 * change events â skip the per-field dedupe and go straight to the 1283 * debounced POST. 1284 */ 1285 if (BWFAN_Public.is_block_checkout) { 1286 if (BWFAN_Public.checkout_fields.indexOf(field_name) === -1 && field_name !== 'billing_email') { 1287 return; 1288 } 1289 if (interval !== null) clearTimeout(interval); 1290 interval = setTimeout(BWFAN_Public.bwfan_get_checkout_data, 300); 1291 return; 1292 } 1293 1294 var field_value = $(this).val(); 1295 1296 /** for checking checkbox fields **/ 1297 if ($(this).attr('type') == 'checkbox') { 1298 field_value = $(this).prop('checked') ? 1 : 0; 1299 } 1300 1301 if (BWFAN_Public.checkout_fields.indexOf(field_name) === -1) { 1302 return; 1303 } 1304 1305 /** 1306 * Dedupe against the last POSTed values, not checkout_fields_data: 1307 * bwfan_captureCheckoutField is delegated on the form, so it runs 1308 * earlier in the same event's bubble path and has already synced 1309 * the live cache â a live-cache comparison always reads "unchanged" 1310 * and the POST would never fire (e.g. first/last name edits). 1311 */ 1312 if (BWFAN_Public.last_posted_fields_data[field_name] === field_value) { 1313 return; 1314 } 1315 1316 if (interval !== null) { 1317 clearTimeout(interval); 1318 } 1319 interval = setTimeout(BWFAN_Public.bwfan_get_checkout_data, 100); 1320 }); 1321 1322 if (typeof bwfan_unsubscribe_preference !== 'undefined' && 'one_click' === bwfan_unsubscribe_preference) { 1323 var urlParams = new URLSearchParams(window.location.search); 1324 var recipient = $('#bwfan_unsubscribe_recipient'); 1325 recipient = (0 === recipient.length) ? urlParams.get('subscriber_recipient') : recipient.html(); 1326 var uid = $('#bwfan_form_uid_id'); 1327 uid = (0 === uid.length) ? urlParams.get('bwfan_form_uid_id') : uid.val(); 1328 if ((uid !== '' && uid !== null) || (recipient !== '' && recipient !== null)) { 1329 $('#bwfan_unsubscribe').click(); 1330 } 1331 1332 if (uid !== '' && uid !== null) { 1333 var cookieExpireDate = new Date(); 1334 cookieExpireDate.setFullYear(cookieExpireDate.getFullYear() + 1); 1335 document.cookie = '_fk_contact_uid=' + uid + '; expires=' + cookieExpireDate.toUTCString() + ';'; 1336 } 1337 } 1338 }); 1339 var updateCartTimeout = null; 1340 $(document).on('updated_checkout', function () { 1341 if (updateCartTimeout !== null) { 1342 clearTimeout(updateCartTimeout); 1343 } 1344 updateCartTimeout = setTimeout(function (){ 1345 var email = $('#billing_email').val(); 1346 if ((!email || email === '') && BWFAN_Public && BWFAN_Public.is_block_checkout) { 1347 email = $('#email').val(); 1348 } 1349 if (email && email !== '') { 1350 BWFAN_Public.bwfan_process_email(email); 1351 } 1352 }, 500); 1353 }); 1354 1355 /** 1356 * WC Blocks fires its own store_api_checkout / wc-blocks_checkout_render events on every Store API 1357 * push. Re-fire process_email on a debounce so the cart row gets touched on cart/shipping changes. 1358 */ 1359 $(document).on('wc-blocks_checkout_render wc-blocks-checkout-update', function () { 1360 if (!BWFAN_Public || !BWFAN_Public.is_block_checkout) return; 1361 var email = $('#email').val(); 1362 if (email && email !== '') { 1363 BWFAN_Public.bwfan_process_email(email); 1364 } 1365 }); 1366 1367 /** 1368 * Restore the previously-selected payment method on the block checkout after recovery. 1369 * 1370 * Modern WC Blocks keeps the active payment method inside the wc/store/payment data store. 1371 * React-controlled radios ignore native DOM changes, so we must drive the store directly. 1372 * We subscribe to the store so we detect when payment methods have initialized and when 1373 * our target becomes active, then stop. 1374 */ 1375 (function () { 1376 if (typeof bwfanParamspublic === 'undefined' || parseInt(bwfanParamspublic._is_block_based_checkout) <= 0) return; 1377 var slug = bwfanParamspublic.bwfan_restored_payment_method; 1378 if (!slug) return; 1379 1380 var applied = false;
1381 var attempts = 0; 1382 var MAX_ATTEMPTS = 40; 1383 1384 var setNativeValue = function (el, value) { 1385 var setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'checked').set; 1386 setter.call(el, value); 1387 }; 1388 1389 var tryDataStore = function () { 1390 if (!window.wp || !wp.data) return false; 1391 try { 1392 var paymentStore = wp.data.dispatch('wc/store/payment'); 1393 var paymentSelect = wp.data.select('wc/store/payment'); 1394 if (!paymentStore || !paymentSelect) return false; 1395 1396 if (typeof paymentSelect.paymentMethodsInitialized === 'function' && !paymentSelect.paymentMethodsInitialized()) { 1397 return false; 1398 } 1399 1400 var available = paymentSelect.getAvailablePaymentMethods ? paymentSelect.getAvailablePaymentMethods() : {}; 1401 if (!available || !Object.prototype.hasOwnProperty.call(available, slug)) { 1402 return false; 1403 } 1404 1405 var active = paymentSelect.getActivePaymentMethod ? paymentSelect.getActivePaymentMethod() : ''; 1406 if (active === slug) { 1407 applied = true; 1408 return true; 1409 } 1410 1411 if (typeof paymentStore.__internalSetActivePaymentMethod === 'function') { 1412 paymentStore.__internalSetActivePaymentMethod(slug); 1413 } else if (typeof paymentStore.setActivePaymentMethod === 'function') { 1414 paymentStore.setActivePaymentMethod(slug); 1415 } 1416 return true; // dispatched; let subscriber confirm on next tick 1417 } catch (e) { 1418 return false; 1419 } 1420 }; 1421 1422 var tryDomFallback = function () { 1423 var $radio = $('.wp-block-woocommerce-checkout-payment-block input[type=radio][value="' + slug + '"], .wc-block-components-payment-method input[type=radio][value="' + slug + '"]').first(); 1424 if ($radio.length && !$radio.is(':checked')) { 1425 setNativeValue($radio[0], true); 1426 $radio[0].dispatchEvent(new Event('change', { bubbles: true })); 1427 } 1428 }; 1429 1430 var tick = function () { 1431 if (applied || attempts++ > MAX_ATTEMPTS) { 1432 if (unsub) unsub(); 1433 if (poll) clearInterval(poll); 1434 return; 1435 } 1436 var storeHandled = tryDataStore(); 1437 if (!storeHandled) { 1438 tryDomFallback(); 1439 } 1440 }; 1441 1442 var unsub = null; 1443 if (window.wp && wp.data && typeof wp.data.subscribe === 'function') { 1444 unsub = wp.data.subscribe(function () { 1445 if (applied) { 1446 if (unsub) unsub(); 1447 return; 1448 } 1449 try { 1450 var paymentSelect = wp.data.select('wc/store/payment'); 1451 if (paymentSelect && typeof paymentSelect.getActivePaymentMethod === 'function') { 1452 if (paymentSelect.getActivePaymentMethod() === slug) { 1453 applied = true; 1454 if (unsub) unsub(); 1455 if (poll) clearInterval(poll); 1456 } 1457 } 1458 } catch (e) {} 1459 }, 'wc/store/payment'); 1460 } 1461 1462 $(window).on('load', function () { setTimeout(tick, 300); }); 1463 var poll = setInterval(tick, 350); 1464 })(); 1465 $("#bwfan-tyb-save-btn").on('click', function () { 1466 var $this = $(this); 1467 var $parent = $this.parents(".bwfan-tyb-wrap"); 1468 var res_msg_selector = $parent.find('.bwfan-tyb-msg'); 1469 1470 // Get values from the dropdown fields if they exist 1471 var yearSelect = $parent.find('select[name="bwfan_birthday_date_yy"]'); 1472 var monthSelect = $parent.find('select[name="bwfan_birthday_date_mm"]'); 1473 var daySelect = $parent.find('select[name="bwfan_birthday_date_dd"]'); 1474 1475 // Check if the dropdowns exist 1476 var dropdownsExist = yearSelect.length && monthSelect.length && daySelect.length; 1477 var year = '', month = '', day = ''; 1478 // If dropdowns exist, validate their values 1479 if (dropdownsExist) {
1480 year = yearSelect.val(); 1481 month = monthSelect.val(); 1482 day = daySelect.val(); 1483 } 1484 1485 $this.addClass('bwfan-btn-spin'); 1486 $.ajax({ 1487 type: "post", 1488 dataType: "json", 1489 url: bwfanParamspublic.ajax_url, 1490 data: { 1491 "order_id": $parent.find('#bwfan-order-id').val(), 1492 "birthday": $parent.find('#bwfan-tyb-field').val(), 1493 "bwfan_birthday_date_yy": year, 1494 "bwfan_birthday_date_mm": month, 1495 "bwfan_birthday_date_dd": day, 1496 "cid": $parent.find('#bwfan-cid').val(), 1497 "nonce": $parent.find('#bwfan-tyb-nonce').val(), 1498 "action": "save_birthday_on_thankyou" 1499 }, 1500 success: function (response) { 1501 $this.removeClass('bwfan-btn-spin'); 1502 1503 res_msg_selector.fadeIn().html(response.msg); 1504 1505 if (2 === response.status) { 1506 res_msg_selector.addClass('bwfan-tyb-error'); 1507 } else { 1508 res_msg_selector.removeClass('bwfan-tyb-error'); 1509 } 1510 1511 setTimeout(function () { 1512 res_msg_selector.fadeOut("slow"); 1513 }, 2500); 1514 } 1515 }); 1516 }); 1517 $(document).on('focus', '#bwfan_birthday_date', function () { 1518 this.type = this.getAttribute("type") !== "date" ? 'date' : this.getAttribute("type"); 1519 setTimeout(() => { 1520 this.showPicker(); 1521 }, 500); 1522 }); 1523 1524 // If unsubscribe form exists, don't run one-click unsubscribe 1525 if (0 !== $('form#bwfan_unsubscribe_fields').length) { 1526 return; 1527 } 1528 1529 // If manage profile form exists, don't run one-click unsubscribe (this is NOT the unsubscribe page) 1530 if (0 !== $('#bwfan_manage_profile_update').length || 0 !== $('#bwfan_manage_profile_fields').length) { 1531 return; 1532 } 1533 1534 let urlParams = new URLSearchParams(window.location.search); 1535 let automation_id = urlParams.get('automation_id'); 1536 let broadcast_id = urlParams.get('broadcast_id'); 1537 let form_feed_id = urlParams.get('form_feed_id'); 1538 let sid = urlParams.get('sid'); 1539 let uid = urlParams.get('uid'); 1540 1541 if (uid === null || uid === '') { 1542 return; 1543 } 1544 1545 $.ajax({ 1546 method: 'post', 1547 url: bwfanParamspublic.ajax_url, 1548 datatype: "JSON", 1549 data: { 1550 action: 'bwfan_unsubscribe_user', 1551 uid: uid, 1552 automation_id: automation_id ? automation_id : 0, 1553 broadcast_id: broadcast_id ? broadcast_id : 0, 1554 form_feed_id: form_feed_id ? form_feed_id : 0, 1555 sid: sid ? sid : 0, 1556 _nonce: bwfanParamspublic.unsubscribe_nonce, 1557 one_click: 1, 1558 }, 1559 success: function (response) { 1560 1561 } 1562 }); 1563})(jQuery); 1564 1565/* Deserialize the form object */ 1566function bwfanac_deserialize_obj(query) { 1567 var setValue = function (root, path, value) { 1568 if (path.length > 1) { 1569 var dir = path.shift(); 1570 if (typeof root[dir] == 'undefined') { 1571 root[dir] = path[0] === '' ? [] : {}; 1572 } 1573 1574 arguments.callee(root[dir], path, value); 1575 } else { 1576 if (root instanceof Array) { 1577 root.push(value); 1578 } else { 1579 root[path] = value; 1580 } 1581 } 1582 }; 1583 1584 var nvp = query.split('&'); 1585 var data = {}; 1586 1587 for (var i = 0; i < nvp.length; i++) { 1588 var pair = nvp[i].split('='); 1589 var name = pair[0]; 1590 var value = pair[1]; 1591 1592 try { 1593 name = decodeURIComponent(pair[0]); 1594 value = decodeURIComponent(pair[1]); 1595 } catch (e) { 1596 console.log("Error occurred while decoding (" + nvp[i] + "). Error is: " + e.message); 1597 } 1598 1599 var path = name.match(/(^[^\[]+)(\[.*\]$)?/); 1600 var first = path[1]; 1601 if (path[2]) { 1602 //case of 'array[level1]' || 'array[level1][level2]' 1603 path = path[2].match(/(?=\[(.*)\]$)/)[1].split('][') 1604 } else { 1605 //case of 'name' 1606 path = []; 1607 } 1608 path.unshift(first); 1609 1610 setValue(data, path, value); 1611 } 1612 1613 return data; 1614}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.