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.