PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.16
Yatra – Travel Booking & Tour Operator Software v3.0.16
3.0.16 3.0.15 3.0.14 3.0.14.1 3.0.14.2 3.0.12 3.0.13 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.5.1 3.0.4 3.0.3 3.0.2.9 3.0.2.7 3.0.2.8 3.0.2.6 trunk 1.0.0 2.0.0 2.0.1 All 84 releases
← All changes | assets/js/booking.js +86 -4 3.0.9 → 3.0.16 View file →
@@ -35,8 +35,21 @@
35 35 if (typeof window.sprintf !== 'function') window.sprintf = function (fmt) { return fmt; };
36 36 }
37 37 })();
38 38
39 +// Parse a numeric min/max attribute while preserving a legitimate 0.
40 +// `parseInt(el.getAttribute('max')) || 20` treated max="0" — what a sold-out
41 +// departure renders — as "unset" and fell back to the placeholder ceiling, so
42 +// the guest selector stayed usable and ignored the departure's real capacity.
43 +// Only an absent or non-numeric value may fall back. Installed once and shared
44 +// by every frontend script, so load order does not matter.
45 +if (typeof window.yatraNumOr !== 'function') {
46 + window.yatraNumOr = function (raw, fallback) {
47 + var parsed = parseInt(raw, 10);
48 + return isNaN(parsed) ? fallback : parsed;
49 + };
50 +}
51 +
39 52 (function($) {
40 53 'use strict';
41 54
42 55 // API configuration - available throughout the module
@@ -64,8 +77,43 @@
64 77
65 78 $(document).ready(function() {
66 79 const $form = $('#yatra-booking-form');
67 80 const $submitBtn = $('#yatra-submit-booking');
81 +
82 + // --- Guest "Create an account" reveal ---
83 + // The optional account-creation block (#yatra-account-fields) ships hidden
84 + // with non-required password inputs. Without this handler the guest can
85 + // tick "Create an account" but never see a password field, so either no
86 + // account is created (empty password) or autofill populates the hidden
87 + // field asymmetrically and the server rejects with "Passwords do not
88 + // match" — with no visible field to correct. Reveal + require the fields
89 + // only when opted in; hide + clear + un-require otherwise so a pure-guest
90 + // submit is never blocked and no stale/autofilled password is sent.
91 + (function initCreateAccountToggle() {
92 + const $accountFields = $('#yatra-account-fields');
93 + const $checkbox = $('#create-account');
94 + if (!$accountFields.length || !$checkbox.length) {
95 + return;
96 + }
97 + const $pw = $accountFields.find('#account_password');
98 + const $pwConfirm = $accountFields.find('#account_password_confirm');
99 +
100 + const applyState = function (checked) {
101 + if (checked) {
102 + $accountFields.show();
103 + $pw.add($pwConfirm).attr('required', 'required');
104 + } else {
105 + $accountFields.hide();
106 + $pw.add($pwConfirm).removeAttr('required').val('');
107 + }
108 + };
109 +
110 + // Honor the current state on load (covers browser-restored checkboxes).
111 + applyState($checkbox.is(':checked'));
112 + $checkbox.on('change', function () {
113 + applyState($(this).is(':checked'));
114 + });
115 + })();
68 116 const isRemainingPayment = Boolean(window.yatraBookingData?.isRemainingPayment) || ($form.data('is-remaining-payment') === 'yes');
69 117 const remainingAmount = isRemainingPayment
70 118 ? parseFloat(window.yatraBookingData?.remainingAmount ?? $form.data('payment-due')) || 0
71 119 : 0;
@@ -682,10 +730,10 @@
682 730
683 731 const $btn = $(this);
684 732 const $input = $('#number-of-travelers');
685 733 let currentValue = parseInt($input.val()) || 1;
686 - const min = parseInt($input.attr('min')) || 1;
687 - const max = parseInt($input.attr('max')) || 20;
734 + const min = window.yatraNumOr($input.attr('min'), 1);
735 + const max = window.yatraNumOr($input.attr('max'), 20);
688 736
689 737 if ($btn.hasClass('yatra-qty-plus')) {
690 738 if (currentValue < max) {
691 739 currentValue++;
@@ -715,10 +763,10 @@
715 763 const $btn = $(this);
716 764 const $row = $btn.closest('.yatra-quantity-row');
717 765 const $input = $row.find('.yatra-qty-input');
718 766 let currentValue = parseInt($input.val()) || 0;
719 - const min = parseInt($input.attr('min')) || 0;
720 - const max = parseInt($input.attr('max')) || 20;
767 + const min = window.yatraNumOr($input.attr('min'), 0);
768 + const max = window.yatraNumOr($input.attr('max'), 20);
721 769
722 770 if ($btn.hasClass('yatra-qty-plus')) {
723 771 if (currentValue < max) {
724 772 currentValue++;
@@ -1104,8 +1152,25 @@
1104 1152 /**
1105 1153 * Submit booking to server
1106 1154 */
1107 1155 function submitBookingToServer(bookingData, originalBtnHtml) {
1156 + // reCAPTCHA v3: attach a token when the booking form is protected,
1157 + // then run the real submit. Wraps every call path (direct submit and
1158 + // the gateway `proceedWithSubmission` hook). No-op unless the operator
1159 + // opted booking into reCAPTCHA (off by default), so payment flows are
1160 + // unchanged otherwise.
1161 + const yatraRc = window.yatraRecaptcha;
1162 + if (yatraRc && yatraRc.protects && yatraRc.protects('booking') && !bookingData.recaptcha_token) {
1163 + yatraRc.execute('booking').then(function (recaptchaToken) {
1164 + if (recaptchaToken) { bookingData.recaptcha_token = recaptchaToken; }
1165 + submitBookingToServerInner(bookingData, originalBtnHtml);
1166 + });
1167 + return;
1168 + }
1169 + submitBookingToServerInner(bookingData, originalBtnHtml);
1170 + }
1171 +
1172 + function submitBookingToServerInner(bookingData, originalBtnHtml) {
1108 1173 const { base: restBase, isPlain } = getRestBase();
1109 1174 const siteBase = (window.yatraBookingData?.siteUrl || window.location.origin || '').replace(/\/$/, '');
1110 1175 let createUrl;
1111 1176 if (isPlain) {
@@ -1842,8 +1907,24 @@
1842 1907 password: password,
1843 1908 confirm_password: confirmPassword
1844 1909 };
1845 1910
1911 + // reCAPTCHA v3: this form posts straight to /auth/register, so it must
1912 + // carry its own token when the operator protects registration — the
1913 + // server rejects an empty one outright, whatever the score threshold.
1914 + // Resolves to '' when reCAPTCHA is off or unavailable, so the normal
1915 + // (unprotected) flow is completely unchanged.
1916 + const yatraRcReg = window.yatraRecaptcha;
1917 + const regTokenPromise = (yatraRcReg && typeof yatraRcReg.protects === 'function'
1918 + && yatraRcReg.protects('registration') && typeof yatraRcReg.execute === 'function')
1919 + ? yatraRcReg.execute('registration')
1920 + : Promise.resolve('');
1921 +
1922 + regTokenPromise.catch(function () { return ''; }).then(function (regToken) {
1923 + if (regToken) {
1924 + registerData.recaptcha_token = regToken;
1925 + }
1926 +
1846 1927 fetch(apiUrl + '/auth/register', {
1847 1928 method: 'POST',
1848 1929 headers: {
1849 1930 'Content-Type': 'application/json',
@@ -1907,8 +1988,9 @@
1907 1988 $btn.prop('disabled', false);
1908 1989 $btnText.show();
1909 1990 $btnLoading.hide();
1910 1991 });
1992 + }); // end reCAPTCHA token wrapper
1911 1993 });
1912 1994
1913 1995 // ---------------------------------------------------------------------
1914 1996 // Date field picker upgrade (fast year navigation for Date of Birth)