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