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/single-trip.js +56 -22 3.0.6 → 3.0.16 View file →
@@ -12,20 +12,43 @@
12 12
13 13 // Translation helpers — idempotent shim matching booking.js / trip.js so
14 14 // `__()` resolves through wp.i18n when wp-i18n is enqueued, otherwise it
15 15 // falls back to the source string.
16 +// Parse a numeric min/max attribute while preserving a legitimate 0.
17 +// `parseInt(el.getAttribute('max')) || 99` treated max="0" — what a sold-out
18 +// departure renders — as "unset" and fell back to the placeholder ceiling, so
19 +// the guest selector stayed usable and ignored the departure's real capacity.
20 +// Only an absent or non-numeric value may fall back. Installed once and shared
21 +// by every frontend script, so load order does not matter.
22 +if (typeof window.yatraNumOr !== 'function') {
23 + window.yatraNumOr = function (raw, fallback) {
24 + var parsed = parseInt(raw, 10);
25 + return isNaN(parsed) ? fallback : parsed;
26 + };
27 +}
28 +
16 29 (function () {
17 - if (typeof window.__ === 'function') return;
18 - if (window.wp && window.wp.i18n && typeof window.wp.i18n.__ === 'function') {
19 - window.__ = function (text, domain) { return window.wp.i18n.__(text, domain || 'yatra'); };
20 - window._n = function (s, p, n, domain) { return window.wp.i18n._n(s, p, n, domain || 'yatra'); };
21 - window._x = function (text, ctx, domain) { return window.wp.i18n._x(text, ctx, domain || 'yatra'); };
22 - window.sprintf = window.sprintf || (window.wp.i18n.sprintf || function (fmt) { return fmt; });
30 + var i18n = (window.wp && window.wp.i18n) ? window.wp.i18n : null;
31 + if (i18n && typeof i18n.__ === 'function') {
32 + window.__ = function (text, domain) { return i18n.__(text, domain || 'yatra'); };
33 + window._n = function (s, p, n, domain) { return i18n._n(s, p, n, domain || 'yatra'); };
34 + window._x = function (text, ctx, domain) { return i18n._x(text, ctx, domain || 'yatra'); };
35 + // Always bind WordPress's own sprintf (it substitutes %d/%s/%1$s).
36 + // Do NOT defer to a pre-existing window.sprintf: a theme or other
37 + // plugin can define an incompatible global that leaves placeholders
38 + // unsubstituted (the cause of literal "%d"/"%s" in the UI).
39 + if (typeof i18n.sprintf === 'function') {
40 + window.sprintf = function () { return i18n.sprintf.apply(i18n, arguments); };
41 + } else if (typeof window.sprintf !== 'function') {
42 + window.sprintf = function (fmt) { return fmt; };
43 + }
23 44 } else {
24 - window.__ = function (text) { return text; };
25 - window._n = function (s, p, n) { return n === 1 ? s : p; };
26 - window._x = function (text) { return text; };
27 - window.sprintf = window.sprintf || function (fmt) { return fmt; };
45 + // wp.i18n not present — install passthroughs only if nothing usable
46 + // exists yet (don't clobber a real binding installed earlier).
47 + if (typeof window.__ !== 'function') window.__ = function (text) { return text; };
48 + if (typeof window._n !== 'function') window._n = function (s, p, n) { return n === 1 ? s : p; };
49 + if (typeof window._x !== 'function') window._x = function (text) { return text; };
50 + if (typeof window.sprintf !== 'function') window.sprintf = function (fmt) { return fmt; };
28 51 }
29 52 })();
30 53
31 54 document.addEventListener('DOMContentLoaded', function() {
@@ -351,10 +374,16 @@
351 374
352 375 updatePricing();
353 376 });
354 377
355 - // Set today's date by default if available, otherwise use trip fallback pricing
356 - const today = new Date().toISOString().split('T')[0];
378 + // Set today's date by default if available, otherwise use trip fallback pricing.
379 + // Build LOCAL YYYY-MM-DD, not toISOString() (which is UTC and can land on
380 + // the wrong calendar day — mis-matching the availability's departure_date
381 + // and pre-selecting the wrong date near midnight).
382 + const _now = new Date();
383 + const today = _now.getFullYear() + '-'
384 + + String(_now.getMonth() + 1).padStart(2, '0') + '-'
385 + + String(_now.getDate()).padStart(2, '0');
357 386 if (!dateInput.value) {
358 387 const availabilities = getSidebarAvailabilities();
359 388 if (availabilities.length > 0) {
360 389 const todayAvailability = availabilities.find(a => a.departure_date === today);
@@ -382,13 +411,18 @@
382 411 categoryInputs.forEach(input => {
383 412 const count = parseInt(input.value) || 0;
384 413 const price = parseFloat(input.dataset.price) || 0;
385 414 const pricingMode = input.dataset.pricingMode || 'per_person';
386 -
415 + const groupOverflow = input.dataset.groupOverflow || 'block';
416 + const maxPax = parseInt(input.dataset.maxPax) || 0;
417 +
387 418 if (pricingMode === 'per_group') {
388 - // Per group: charge flat price once if any travelers
419 + // Per group: one flat price, or per block of maxPax people
420 + // when overflow is "per_block".
389 421 if (count > 0) {
390 - subtotal += price;
422 + subtotal += (groupOverflow === 'per_block' && maxPax > 0)
423 + ? price * Math.ceil(count / maxPax)
424 + : price;
391 425 }
392 426 } else {
393 427 // Per person: charge per traveler
394 428 subtotal += price * count;
@@ -436,9 +470,9 @@
436 470 <div class="yatra-group-discount-badge">
437 471 <svg width="14" height="14" fill="currentColor" viewBox="0 0 20 20">
438 472 <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
439 473 </svg>
440 - Group discount applied: -${window.yatraTripData?.currencySymbol || '$'}${discountAmount.toFixed(2)}
474 + Group discount applied: -${typeof window.yatra_format_price_js === 'function' ? window.yatra_format_price_js(discountAmount) : (window.yatraTripData?.currencySymbol || '$') + discountAmount.toFixed(2)}
441 475 </div>
442 476 `;
443 477 } else if (indicator) {
444 478 indicator.remove();
@@ -538,10 +572,10 @@
538 572 }
539 573
540 574 if (!input) return;
541 575
542 - const min = parseInt(input.getAttribute('min')) || 0;
543 - const max = parseInt(input.getAttribute('max')) || 99;
576 + const min = window.yatraNumOr(input.getAttribute('min'), 0);
577 + const max = window.yatraNumOr(input.getAttribute('max'), 99);
544 578 let value = parseInt(input.value) || 0;
545 579
546 580 // Update value based on button type
547 581 if (btn.classList.contains('yatra-quantity-plus')) {
@@ -597,9 +631,9 @@
597 631 // Regular pricing - sync all num_travelers and enquiry_adults inputs
598 632 const regularInputs = document.querySelectorAll('#num_travelers, #enquiry_adults');
599 633 regularInputs.forEach(input => {
600 634 if (input !== sourceInput) {
601 - const max = parseInt(input.getAttribute('max')) || 99;
635 + const max = window.yatraNumOr(input.getAttribute('max'), 99);
602 636 input.value = Math.min(sourceValue, max);
603 637 }
604 638 });
605 639
@@ -606,9 +640,9 @@
606 640 // Also sync to availability section inputs
607 641 const availabilityInputs = document.querySelectorAll('input[id^="num-travelers-"]');
608 642 availabilityInputs.forEach(input => {
609 643 if (input !== sourceInput) {
610 - const max = parseInt(input.getAttribute('max')) || 99;
644 + const max = window.yatraNumOr(input.getAttribute('max'), 99);
611 645 input.value = Math.min(sourceValue, max);
612 646 }
613 647 });
614 648
@@ -633,15 +667,15 @@
633 667 );
634 668
635 669 categoryInputs.forEach(input => {
636 670 if (input !== sourceInput) {
637 - const max = parseInt(input.getAttribute('max')) || 99;
671 + const max = window.yatraNumOr(input.getAttribute('max'), 99);
638 672 input.value = Math.min(sourceValue, max);
639 673
640 674 // Update button states for this input
641 675 const row = input.closest('.yatra-quantity-row');
642 676 if (row) {
643 - const min = parseInt(input.getAttribute('min')) || 0;
677 + const min = window.yatraNumOr(input.getAttribute('min'), 0);
644 678 const minusBtn = row.querySelector('.yatra-quantity-minus');
645 679 const plusBtn = row.querySelector('.yatra-quantity-plus');
646 680 if (minusBtn) minusBtn.disabled = input.value <= min;
647 681 if (plusBtn) plusBtn.disabled = input.value >= max;