PageSourceSearch

https://zmieniamto.pl/wp-content/plugins/wp-marketing-automations/assets/js/bwfan-public.js?ver=3.8.3

js zmieniamto.pl collected 2026-09-24 15:11:06 UTC 71,706 bytes, 1,603 lines download raw bytes

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.