PageSourceSearch

https://inbreakthrough.org/wp-content/plugins/donations/assets/give-lite-frontend.js?ver=1790287854

js inbreakthrough.org collected 2026-09-24 22:11:24 UTC 26,553 bytes, 710 lines download raw bytes

1// Shared minimum donation amount helper (reads settings with fallback to 500)
2function getMinAmount() {
3    const isMonthly = jQuery('#pills-monthly-tab').hasClass('active') || jQuery("input[name='payment_type']:checked").val() === 'monthly';
4    let minVal = 500;
5
6    if (isMonthly) {
7        const inputVal = jQuery('input[name="min_monthly_amount"]').val();
8        if (inputVal && !isNaN(parseFloat(inputVal)) && parseFloat(inputVal) > 0) {
9            minVal = parseFloat(inputVal);
10        } else if (typeof GiveLite !== 'undefined' && GiveLite.min_monthly_amount && parseFloat(GiveLite.min_monthly_amount) > 0) {
11            minVal = parseFloat(GiveLite.min_monthly_amount);
12        }
13    } else {
14        const inputVal = jQuery('input[name="min_once_amount"]').val();
15        if (inputVal && !isNaN(parseFloat(inputVal)) && parseFloat(inputVal) > 0) {
16            minVal = parseFloat(inputVal);
17        } else if (typeof GiveLite !== 'undefined' && GiveLite.min_once_amount && parseFloat(GiveLite.min_once_amount) > 0) {
18            minVal = parseFloat(GiveLite.min_once_amount);
19        }
20    }
21
22    return (minVal > 0) ? minVal : 500;
23}
24
25jQuery(function ($) {
26
27
28    // ---------------- UTM CAPTURE ----------------
29    const urlParams = new URLSearchParams(window.location.search);
30
31    const utm_source = urlParams.get("utm_source") || localStorage.getItem("utm_source") || "";
32    const utm_medium = urlParams.get("utm_medium") || "";
33    const utm_campaign = urlParams.get("utm_campaign") || "";
34    const utm_term = urlParams.get("utm_term") || "";
35    const utm_content = urlParams.get("utm_content") || "";
36    localStorage.setItem("utm_source", utm_source);
37
38    const radioButtons = document.querySelectorAll(".amount-radio-btn");
39    const otherAmount = document.getElementById("otherAmount");
40
41    if (radioButtons.length > 0) {
42        // Clear radio when user clicks on "Other Amount"
43        otherAmount.addEventListener("focus", () => {
44            radioButtons.forEach(radio => radio.checked = false);
45        });
46    }
47    // Clear otherAmount if any radio selected
48    radioButtons.forEach(radio => {
49        radio.addEventListener("change", () => {
50            otherAmount.value = "";
51        });
52    });
53
54    function toastMsg(type = 'success', msg = 'Succsesfull') {
55        if ($(".donation-toast").length >= 1) {
56            $('.donation-toast').remove();
57        }
58        var title;
59        if (type == 'd-error') {
60            title = 'Error';
61        } else if (type == 'd-success') {
62            title = 'Success';
63        } else {
64            title = 'Warning';
65        }
66        var html = `
67			<div class=" donation-toast active ${type} ">
68				<div class="toast-content">
69					<i class="fas fa-solid fa-check check"></i>
70					<div class="message">
71						<span class="text text-1 text-capitalize">${title}</span>
72						<span class="text text-2">${msg}</span>
73					</div>
74				</div>
75				<i class="fa-solid fa-xmark close text-dark"></i>
76				<!-- Remove 'active' class, this is just to show in Codepen thumbnail -->
77				<div class="progress active"></div>
78			</div>
79		`;
80
81        $('body').append(html);
82
83        const $toast = $(".donation-toast");
84        const $closeIcon = $(".donation-toast .close");
85        const $progress = $(".donation-toast .progress");
86
87        let timer1, timer2;
88        $toast.addClass("active");
89        $progress.addClass("active");
90        timer1 = setTimeout(function () {
91            $toast.removeClass("active");
92        }, 5000); // 1s = 1000 ms
93
94        timer2 = setTimeout(function () {
95            $progress.removeClass("active ");
96        }, 5300);
97
98        $closeIcon.on("click", function () {
99            $toast.removeClass("active");
100
101            setTimeout(function () {
102                $progress.removeClass("active");
103            }, 300);
104
105            clearTimeout(timer1);
106            clearTimeout(timer2);
107        });
108    }
109
110    function isValidPAN(pan) {
111        const panRegex = /^[A-Z]{5}[0-9]{4}[A-Z]$/;
112        return panRegex.test(pan.trim());
113    }
114
115    let giveLitePaymentInProgress = false;
116
117    $("input[name='phone']").on("input", function () {
118        let value = $(this).val();
119
120        // keep only numbers
121        value = value.replace(/[^0-9]/g, "");
122
123        // limit to 12 digits
124        if (value.length > 12) {
125            value = value.slice(0, 12);
126        }
127
128        $(this).val(value);
129    });
130
131    $("#give-lite-donation-form").on("submit", async function (e) {
132        e.preventDefault();
133
134        let form = $(this);
135
136        let name = form.find("input[name='name']").val().trim();
137        let phone = form.find("input[name='phone']").val().trim();
138        let email = form.find("input[name='email']").val().trim();
139        let payment_type = form.find("input[name='payment_type']:checked").val() || ($('#pills-monthly-tab').hasClass('active') ? 'monthly' : 'once');
140        let otherAmountVal = form.find("input[name='otherAmount']").val().trim();
141
142        let activeTabRadio = (payment_type === "monthly")
143            ? form.find("#pills-monthly input[name='amount']:checked")
144            : form.find("#pills-once input[name='amount']:checked");
145        let amount = activeTabRadio.val() || otherAmountVal;
146
147        let address = form.find("input[name='billing_address']").val().trim();
148        let city = form.find("input[name='billing_city']").val().trim();
149        let state = form.find("input[name='billing_state']").val().trim();
150        let pan = form.find("input[name='billing_pan']").val().trim();
151
152        let termsChecked = form.find("input[name='terms']").is(":checked");
153        let citizenChecked = form.find("input[name='citizen']").is(":checked");
154        let months = '0';
155
156        let once_amounts = form.find("input[name='once_amounts']").val() || "";
157        let monthly_amounts = form.find("input[name='monthly_amounts']").val() || "";
158        let min_once_amount = form.find("input[name='min_once_amount']").val() || "";
159        let min_monthly_amount = form.find("input[name='min_monthly_amount']").val() || "";
160        let min_amount = getMinAmount();
161
162        let inputEmpty = false;
163
164        // 🔥 ONLY CHECK VISIBLE INPUTS NOW
165        form.find("input").each(function () {
166            let nameAttr = $(this).attr("name");
167
168            // skip optional fields
169            if (["otherAmount", "amount", "payment_type"].includes(nameAttr)) return;
170
171            let val = $(this).val()?.trim();
172
173            if (!val && $(this).attr("type") !== "checkbox") {
174                $(this).css('borderBottom', '1px solid red');
175                inputEmpty = true;
176            } else {
177                $(this).css('borderColor', '');
178            }
179        });
180
181        if (!name || !email || !phone || !address || !city || !state || !pan) {
182            toastMsg('d-error', 'Please fill all required fields.');
183            return;
184        }
185
186        if (!isValidPAN(pan)) {
187            toastMsg('d-error', 'Invalid PAN (ABCDE1234F)');
188            return;
189        }
190
191        if (!amount) {
192            toastMsg('d-error', 'Please select or enter donation amount.');
193            return;
194        }
195
196        if (!termsChecked) {
197            toastMsg('d-error', 'Please accept terms & conditions.');
198            form.find("input[name='terms']").closest('.form-check').css('color', 'red');
199            return;
200        } else {
201            form.find("input[name='terms']").closest('.form-check').css('color', '');
202        }
203
204        if (!citizenChecked) {
205            toastMsg('d-error', 'Please confirm you are an Indian passport holder.');
206            form.find("input[name='citizen']").closest('.form-check').css('color', 'red');
207            return;
208        } else {
209            form.find("input[name='citizen']").closest('.form-check').css('color', '');
210        }
211
212        // =========================
213        // MONTHLY LOGIC (UNCHANGED)
214        // =========================
215        if (payment_type === "monthly") {
216            let monthsInput = form.find("input[name='months']");
217
218            if (monthsInput.length) {
219                months = monthsInput.val().trim();
220
221                if (!months) {
222                    toastMsg('d-error', 'Please choose the number of years.');
223                    return;
224                }
225            } else {
226                months = '12'; // fallback
227            }
228        }
229
230
231        if (giveLitePaymentInProgress) {
232            return;
233        }
234        giveLitePaymentInProgress = true;
235
236        let submitBtn = form.find("button[type='submit']");
237        submitBtn.prop("disabled", true).text("Please wait...");
238
239        try {
240            const sessionResponse = await $.ajax({
241                url: GiveLite.ajax_url,
242                type: "POST",
243                data: {
244                    action: "give_lite_create_donation_session",
245                    security: GiveLite.nonce,
246                    name: name,
247                    email: email,
248                    phone: phone,
249                    amount: amount,
250                    otherAmount: otherAmountVal,
251                    address: address,
252                    pincode: "", // removed
253                    city: city,
254                    state: state,
255                    pan: pan,
256                    dob: "", // removed
257                    months: months,
258                    payment_type: payment_type,
259                    once_amounts: once_amounts,
260                    monthly_amounts: monthly_amounts,
261                    min_amount: min_amount,
262                    min_once_amount: min_once_amount,
263                    min_monthly_amount: min_monthly_amount,
264
265                    // ---------------- UTM ----------------
266                    utm_source: utm_source,
267                    utm_medium: utm_medium,
268                    utm_campaign: utm_campaign,
269                    utm_term: utm_term,
270                    utm_content: utm_content
271                }
272            });
273
274            if (!sessionResponse.success) {
275                toastMsg('d-warning', sessionResponse.data.message);
276                giveLitePaymentInProgress = false;
277                submitBtn.prop("disabled", false).text("CONTINUE TO PAYMENT");
278                return;
279            }
280
281            let data = sessionResponse.data;
282            const donationId = data.donation_id;
283
284            if (payment_type === "monthly") {
285                let options = {
286                    key: data.key,
287                    subscription_id: data.subscription_id,
288                    name: "Donation",
289                    description: "Thank you ❤️",
290                    prefill: { name: name, email: email, contact: phone },
291                    theme: { color: "#d32f2f" },
292                    handler: async function (paymentResponse) {
293                        try {
294                            const res = await $.ajax({
295                                url: GiveLite.ajax_url,
296                                type: "POST",
297                                data: {
298                                    action: 'give_lite_verify_payment_session',
299                                    security: GiveLite.nonce,
300                                    donation_id: donationId,
301                                    razorpay_payment_id: paymentResponse.razorpay_payment_id,
302                                    razorpay_subscription_id: paymentResponse.razorpay_subscription_id,
303                                    razorpay_signature: paymentResponse.razorpay_signature
304                                }
305                            });
306
307                            window.location.href = res && res.success
308                                ? `${GiveLite.site_url}/thank-you?order_id=${res.data.subscription_id}&mode=monthly`
309                                : `${GiveLite.site_url}/payment-cancelled/`;
310                        } catch (err) {
311                            window.location.href = `${GiveLite.site_url}/payment-cancelled/`;
312                        }
313                    },
314                    modal: {
315                        ondismiss: async function () {
316                            try {
317                                await $.ajax({
318                                    url: GiveLite.ajax_url,
319                                    type: "POST",
320                                    data: {
321                                        action: 'give_lite_cancel_donation',
322                                        security: GiveLite.nonce,
323                                        donation_id: donationId
324                                    }
325                                });
326                            } catch (err) {
327                                console.error('Error canceling donation:', err);
328                            }
329                            window.location.href = `${GiveLite.site_url}/payment-cancelled/`;
330                        }
331                    }
332                };
333
334                new Razorpay(options).open();
335                return;
336            }
337
338            // One-time
339            let options = {
340                key: data.key,
341                amount: data.amount,
342                currency: data.currency,
343                order_id: data.order_id,
344                name: "Donation",
345                description: "Thank you ❤️",
346                prefill: { name: name, email: email, contact: phone },
347                theme: { color: "#d32f2f" },
348                handler: async function (paymentResponse) {
349                    try {
350                        const res = await $.ajax({
351                            url: GiveLite.ajax_url,
352                            type: "POST",
353                            data: {
354                                action: 'give_lite_verify_payment_session',
355                                security: GiveLite.nonce,
356                                donation_id: donationId,
357                                razorpay_payment_id: paymentResponse.razorpay_payment_id,
358                                razorpay_order_id: paymentResponse.razorpay_order_id,
359                                razorpay_signature: paymentResponse.razorpay_signature
360                            }
361                        });
362                        console.log(res);
363                        window.location.href = res && res.success
364                            ? `${GiveLite.site_url}/thank-you/`
365                            : `${GiveLite.site_url}/payment-cancelled/`;
366                    } catch (err) {
367                        window.location.href = `${GiveLite.site_url}/payment-cancelled/`;
368                    }
369                },
370                modal: {
371                    ondismiss: async function () {
372                        try {
373                            await $.ajax({
374                                url: GiveLite.ajax_url,
375                                type: "POST",
376                                data: {
377                                    action: 'give_lite_cancel_donation',
378                                    security: GiveLite.nonce,
379                                    donation_id: donationId
380                                }
381                            });
382                        } catch (err) {
383                            console.error('Error canceling donation:', err);
384                        }
385                        window.location.href = `${GiveLite.site_url}/payment-cancelled/`;
386                    }
387                }
388            };
389
390            new Razorpay(options).open();
391        } catch (err) {
392            toastMsg('d-warning', 'Payment initialization failed.');
393            giveLitePaymentInProgress = false;
394            submitBtn.prop("disabled", false).text("CONTINUE TO PAYMENT");
395        }
396    });
397
398    // -------------------------------
399    // PINCODE AUTO-FETCH CITY/STATE
400    // -------------------------------
401    function debounce(func, delay) {
402        let timer;
403        return function (...args) {
404            clearTimeout(timer);
405            timer = setTimeout(() => func.apply(this, args), delay);
406        };
407    }
408
409    const fetchState = debounce(async function () {
410
411        const city = $('input[name="billing_city"]').val().trim();
412
413        if (city.length >= 3) {
414
415            try {
416
417                const response = await fetch(
418                    `/wp-admin/admin-ajax.php?action=get_state_by_city&city=${encodeURIComponent(city)}`
419                );
420
421                const result = await response.json();
422
423                if (
424                    result[0]?.Status === "Success" &&
425                    result[0]?.PostOffice?.length
426                ) {
427
428                    const postOffice = result[0].PostOffice[0];
429
430                    $('input[name="billing_state"]').val(postOffice.State);
431                }
432
433            } catch (error) {
434
435                console.error('City API Error:', error);
436            }
437        }
438
439    }, 600);
440
441    $(document).on('input', 'input[name="billing_city"]', fetchState);
442
443    // -------------------------------
444    // "Donate" button — gate to details form
445    // Validates only the field belonging to the ACTIVE tab, so a donor
446    // can never proceed with an amount below GL_MIN_AMOUNT (one-time)
447    // or a missing years value (monthly).
448    // -------------------------------
449    function getActiveTabCheckedRadio() {
450        const isMonthly = $('#pills-monthly-tab').hasClass('active');
451        return isMonthly
452            ? $('#pills-monthly input[name="amount"]:checked')
453            : $('#pills-once input[name="amount"]:checked');
454    }
455
456    $('.openinfo').on('click', function (e) {
457        if ($(this).hasClass('gl-disabled')) {
458            e.preventDefault();
459            e.stopPropagation();
460            return false;
461        }
462
463        const monthly = $('#pills-monthly-tab').hasClass('active');
464
465        if (monthly) {
466            const $months = $('input[name="months"]');
467            if ($months.length && !$months.val().trim()) {
468                $('#glMonthsError').show();
469                $months.css('border-color', 'red').trigger('focus');
470                toastMsg('d-warning', 'Please choose the number of years.');
471                return;
472            }
473        }
474
475        const minAmt = getMinAmount();
476        const $activeRadio = getActiveTabCheckedRadio();
477        const radioChecked = $activeRadio.length > 0;
478        const radioVal = radioChecked ? parseFloat($activeRadio.val()) : 0;
479        const otherRaw = $('#otherAmount').val().trim();
480        const otherVal = parseFloat(otherRaw);
481        const hasOtherAmount = otherRaw !== '' && !isNaN(otherVal);
482
483        if (!radioChecked && !hasOtherAmount) {
484            toastMsg('d-warning', 'Please Select or fill the amount');
485            return;
486        }
487
488        if (radioChecked && radioVal < minAmt) {
489            toastMsg('d-warning', `Amount should be greater than or equal to ₹${minAmt}`);
490            return;
491        }
492
493        if (!radioChecked && hasOtherAmount && otherVal < minAmt) {
494            $('#glOtherAmountError').text(`Amount should be greater than or equal to ₹${minAmt}`).show();
495            $('#otherAmount').css('border-color', 'red');
496            toastMsg('d-warning', `Amount should be greater than or equal to ₹${minAmt}`);
497            return;
498        }
499
500        $('.donation-box').slideUp(400, function () {
501            $('.right-form').slideDown(400);
502        });
503    });
504
505    $('.backbtn').on('click', function () {
506        $(".right-form").slideUp(400, function () {
507            $('.donation-box').slideDown(400);
508        });
509    });
510
511    // Automatically check corresponding radio inputs & uncheck other tab radios when Give Once / Give Monthly tabs are clicked
512    $('#pills-once-tab').on('shown.bs.tab click', function () {
513        $("input[name='payment_type'][value='once']").prop('checked', true);
514        $('#pills-monthly input[name="amount"]').prop('checked', false);
515    });
516    $('#pills-monthly-tab').on('shown.bs.tab click', function () {
517        $("input[name='payment_type'][value='monthly']").prop('checked', true);
518        $('#pills-once input[name="amount"]').prop('checked', false);
519    });
520
521});
522
523
524
525/**
526 * Give Lite - Minimum Amount / Years Validation (Standalone Add-on)
527 * Does NOT modify or depend on the payment AJAX flow above.
528 * Handles: live field feedback, Donate button enable/disable state,
529 * and resetting validation state whenever the donor switches tabs
530 * (so errors from one tab never bleed into the other).
531 */
532jQuery(function ($) {
533
534    const $glOtherAmount = $('#otherAmount');
535    const $glDonateBtn = $('.openinfo');
536
537    if ($glOtherAmount.length === 0) {
538        return; // form not present on this page, do nothing
539    }
540
541    function updateErrorText() {
542        const minAmt = getMinAmount();
543        if ($('#glOtherAmountError').length) {
544            $('#glOtherAmountError').text(`Amount should be greater than or equal to ₹${minAmt}`);
545        }
546    }
547
548    // Inject error message elements once
549    if ($('#glOtherAmountError').length === 0) {
550        $glOtherAmount.after(
551            `<div id="glOtherAmountError" style="color:red;font-size:13px;margin-top:5px;display:none;">Amount should be greater than or equal to ₹${getMinAmount()}</div>`
552        );
553    }
554
555    if ($('#glMonthsError').length === 0) {
556        $('input[name="months"]').after(
557            '<div id="glMonthsError" style="color:red;font-size:13px;margin-top:5px;display:none;">Please choose the number of years.</div>'
558        );
559    }
560
561    // Inject disabled-state CSS once
562    if ($('#glDisabledBtnStyle').length === 0) {
563        $('head').append(
564            '<style id="glDisabledBtnStyle">.openinfo.gl-disabled{opacity:0.5;pointer-events:none;cursor:not-allowed;}</style>'
565        );
566    }
567
568    function isMonthlyActive() {
569        return $('#pills-monthly-tab').hasClass('active');
570    }
571
572    function getActiveTabCheckedRadio() {
573        return isMonthlyActive()
574            ? $('#pills-monthly input[name="amount"]:checked')
575            : $('#pills-once input[name="amount"]:checked');
576    }
577
578    function glIsAmountValid() {
579        const minAmt = getMinAmount();
580        updateErrorText();
581
582        const $checkedRadio = getActiveTabCheckedRadio();
583        if ($checkedRadio.length > 0) {
584            const radioVal = parseFloat($checkedRadio.val());
585            if (!isNaN(radioVal) && radioVal >= minAmt) {
586                $('#glOtherAmountError').hide();
587                $glOtherAmount.css('border-color', '');
588                return true;
589            }
590        }
591
592        let rawVal = $glOtherAmount.val().trim();
593
594        if (!rawVal) {
595            $('#glOtherAmountError').hide();
596            $glOtherAmount.css('border-color', '');
597            return true;
598        }
599
600        let val = parseFloat(rawVal);
601
602        if (isNaN(val) || val < minAmt) {
603            $('#glOtherAmountError').text(`Amount should be greater than or equal to ₹${minAmt}`).show();
604            $glOtherAmount.css('border-color', 'red');
605            return false;
606        }
607
608        $('#glOtherAmountError').hide();
609        $glOtherAmount.css('border-color', '');
610        return true;
611    }
612
613    // Button is disabled based on whichever tab is currently active
614    function glUpdateDonateButtonState() {
615        let disable;
616        const minAmt = getMinAmount();
617        const $checkedRadio = getActiveTabCheckedRadio();
618        const radioChecked = $checkedRadio.length > 0;
619        const radioVal = radioChecked ? parseFloat($checkedRadio.val()) : 0;
620        const rawVal = $glOtherAmount.val().trim();
621        const val = parseFloat(rawVal);
622        const hasValidAmount = (radioChecked && radioVal >= minAmt) || (rawVal !== '' && !isNaN(val) && val >= minAmt);
623
624        if (isMonthlyActive()) {
625            const yearsFilled = $('input[name="months"]').val().trim() !== '';
626            disable = !yearsFilled || !hasValidAmount;
627        } else {
628            disable = !hasValidAmount;
629        }
630
631        $glDonateBtn.toggleClass('gl-disabled', disable);
632    }
633
634    // Clears both fields' error states — used whenever the donor switches tabs
635    function glResetValidationState() {
636        $('#glOtherAmountError').hide();
637        $glOtherAmount.css('border-color', '');
638        $('#glMonthsError').hide();
639        $('input[name="months"]').css('border-color', '');
640    }
641
642    // Reset + re-evaluate button state whenever the donor switches tabs
643    $('#pills-once-tab, #pills-monthly-tab').on('shown.bs.tab click', function () {
644        // Uncheck radio buttons in the inactive tab to prevent cross-tab contamination
645        if ($(this).attr('id') === 'pills-once-tab') {
646            $('#pills-monthly input[name="amount"]').prop('checked', false);
647        } else {
648            $('#pills-once input[name="amount"]').prop('checked', false);
649        }
650
651        glResetValidationState();
652        updateErrorText();
653        glIsAmountValid();
654        glUpdateDonateButtonState();
655    });
656
657    // Live validation + button state while typing amount
658    $(document).on('input', '#otherAmount', function () {
659        glIsAmountValid();
660        glUpdateDonateButtonState();
661    });
662
663    // Live validation + button state while typing years
664    $(document).on('input', 'input[name="months"]', function () {
665        if ($(this).val().trim() !== '') {
666            $('#glMonthsError').hide();
667            $(this).css('border-color', '');
668        }
669        glUpdateDonateButtonState();
670    });
671
672    // Clear error + update button state when a preset radio amount is chosen
673    $(document).on('change', '.amount-radio-btn', function () {
674        glIsAmountValid();
675        glUpdateDonateButtonState();
676    });
677
678    // Set initial state on page load
679    updateErrorText();
680    glUpdateDonateButtonState();
681});
682
683document.querySelectorAll('.donation-section-wrapper input[type="number"]').forEach(input => {
684    input.addEventListener('wheel', function (e) {
685        e.target.blur();
686    });
687});
688
689document.querySelectorAll('.donation-section-wrapper input[type="number"]').forEach(input => {
690    input.addEventListener('wheel', function (e) {
691        e.target.blur();
692    });
693
694    input.addEventListener('keydown', function (e) {
695        if (['e', 'E', '+', '-'].includes(e.key)) {
696            e.preventDefault();
697        }
698    });
699
700    input.addEventListener('paste', function (e) {
701        const pasted = (e.clipboardData || window.clipboardData).getData('text');
702        if (!/^\d+$/.test(pasted)) {
703            e.preventDefault();
704        }
705    });
706
707    input.addEventListener('input', function () {
708        this.value = this.value.replace(/[^0-9]/g, '');
709    });
710});

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.