PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.2.8
Yatra – Travel Booking & Tour Operator Software v3.0.2.8
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
yatra / templates / partials / single-trip / sticky-sidebar.php

sticky-sidebar.php in Yatra – Travel Booking & Tour Operator Software 3.0.2.8, at templates/partials/single-trip/sticky-sidebar.php

597 lines 25.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Mobile Sticky Sidebar Template
4 *
5 * Displays booking card in sticky bottom position on mobile devices
6 * SEO-friendly approach - separate template, no content duplication
7 *
8 * @package Yatra
9 */
10
11 if (!defined('ABSPATH')) {
12 exit;
13 }
14
15 // Expected variables: $trip, $has_availability, $has_traveler_pricing, $base_price, $pricing_type
16
17 // Determine if this is a multi-day trip
18 $is_multi_day = ($trip->getDurationDays() ?? 1) > 1;
19
20 // Check for group discounts availability (premium feature)
21 $sidebar_has_group_discounts = false;
22 $sidebar_group_discounts_data = [];
23 try {
24 if (class_exists('\Yatra\Services\DiscountService') &&
25 method_exists('\Yatra\Services\DiscountService', 'getGroupDiscountsForTrip')) {
26 $discountService = new \Yatra\Services\DiscountService();
27 $groupDiscountsResult = $discountService->getGroupDiscountsForTrip((int) $trip->getId());
28 if (!empty($groupDiscountsResult)) {
29 $sidebar_has_group_discounts = true;
30 $sidebar_group_discounts_data = $groupDiscountsResult;
31 }
32 }
33 } catch (\Exception $e) {
34 $sidebar_has_group_discounts = false;
35 }
36
37 // Prepare availability data for JavaScript
38 $availability_json = [];
39 $booking_mode = $trip->getBookingMode();
40 if ($has_availability) {
41 foreach ($trip->getAvailabilityDates() as $avail) {
42 $availability_json[] = [
43 'id' => (int) $avail->id,
44 'date' => $avail->departure_date,
45 'departure_date' => $avail->departure_date,
46 'return_date' => (isset($avail->return_date) && $avail->return_date !== '')
47 ? $avail->return_date
48 : (isset($avail->arrival_date) ? $avail->arrival_date : null),
49 'price' => $avail->effective_price ?? $avail->original_price,
50 'original_price' => $avail->original_price,
51 'discounted_price' => $avail->discounted_price,
52 'seats_available' => $avail->seats_available,
53 'seats_total' => $avail->seats_total,
54 'status' => $avail->status,
55 'is_limited' => $avail->is_limited ?? false,
56 'is_sold_out' => $avail->is_sold_out ?? false,
57 ];
58 }
59 }
60
61 // Calculate pricing - use pre-computed values from SingleTripController
62 $pricing = [
63 'has_price' => false,
64 'current_price' => '',
65 'original_price' => '',
66 'price_prefix' => '',
67 'has_discount' => false,
68 'raw_current_price' => 0,
69 'raw_original_price' => 0,
70 'is_traveler_based' => $has_traveler_pricing
71 ];
72
73 $discount = [
74 'has_discount' => false,
75 'discount_text' => '',
76 'discount_percentage' => 0
77 ];
78
79 // Use effective_price_min computed in SingleTripController
80 $effective_min = (float) ($trip->effective_price_min ?? 0);
81 $original_min = (float) ($trip->min_category_original_price ?? 0);
82 $max_discount_pct = (int) ($trip->max_discount_percentage ?? 0);
83
84 if ($effective_min > 0) {
85 $pricing['has_price'] = true;
86 $pricing['raw_current_price'] = $effective_min;
87 $pricing['current_price'] = yatra_format_price($effective_min);
88
89 if ($has_traveler_pricing || $has_availability) {
90 $pricing['price_prefix'] = __('From ', 'yatra');
91 }
92
93 if ($max_discount_pct > 0 && $original_min > $effective_min) {
94 $pricing['has_discount'] = true;
95 $pricing['raw_original_price'] = $original_min;
96 $pricing['original_price'] = yatra_format_price($original_min);
97
98 $discount['has_discount'] = true;
99 $discount['discount_percentage'] = $max_discount_pct;
100 $discount['discount_text'] = sprintf(__('Up to %d%%', 'yatra'), $max_discount_pct);
101 }
102 } else {
103 $original = (float) ($trip->getOriginalPrice() ?? 0);
104 $discounted = (float) ($trip->discounted_price ?? 0);
105
106 if ($discounted > 0) {
107 $current = $discounted;
108 } elseif ($original > 0) {
109 $current = $original;
110 } elseif (!empty($trip->getSalePrice()) && (float)$trip->getSalePrice() > 0) {
111 $current = (float) $trip->getSalePrice();
112 } else {
113 $current = 0;
114 }
115
116 if ($current > 0) {
117 $pricing['has_price'] = true;
118 $pricing['raw_current_price'] = $current;
119 $pricing['current_price'] = yatra_format_price($current);
120
121 if ($current < $original && $original > 0) {
122 $pct = round((($original - $current) / $original) * 100);
123
124 // Only show discount if percentage is greater than 0
125 if ($pct > 0) {
126 $pricing['has_discount'] = true;
127 $pricing['raw_original_price'] = $original;
128 $pricing['original_price'] = yatra_format_price($original);
129
130 $discount['has_discount'] = true;
131 $discount['discount_percentage'] = $pct;
132 $discount['discount_text'] = sprintf(__('%d%%', 'yatra'), $pct);
133 }
134 }
135 }
136 }
137 ?>
138 <div class="yatra-mobile-sticky-sidebar" id="yatra-mobile-sticky-sidebar"
139 data-has-availability="<?php echo $has_availability ? 'true' : 'false'; ?>"
140 data-booking-mode="<?php echo esc_attr($booking_mode); ?>"
141 data-is-multi-day="<?php echo $is_multi_day ? 'true' : 'false'; ?>"
142 data-pricing-type="<?php echo esc_attr($pricing_type); ?>"
143 data-availability='<?php echo esc_attr(json_encode($availability_json)); ?>'
144 data-group-discounts='<?php echo esc_attr(json_encode($sidebar_group_discounts_data)); ?>'>
145 <!-- Two-Row Layout -->
146 <div class="yatra-mobile-sticky-content">
147
148 <!-- Row 1: Date, Travelers, and Actions -->
149 <div class="yatra-mobile-row-1">
150 <!-- Group Discount Badge -->
151 <?php if ($sidebar_has_group_discounts): ?>
152 <div class="yatra-mobile-group-badge">
153 <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
154 <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
155 <circle cx="9" cy="7" r="4"></circle>
156 <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
157 <path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
158 </svg>
159 </div>
160 <?php endif; ?>
161
162 <!-- Date Picker - Compact Mobile Version -->
163 <div class="yatra-mobile-date-section">
164 <div class="yatra-mobile-compact-datepicker">
165 <div class="yatra-mobile-field-icon">
166 <?php echo yatra_svg_icon('calendar', 'yatra-mobile-icon'); ?>
167 </div>
168 <input type="text"
169 id="mobile_travel_date"
170 name="travel_date"
171 class="yatra-mobile-datepicker-input yatra-mobile-datepicker"
172 placeholder="<?php esc_attr_e('Date', 'yatra'); ?>"
173 data-min-date="<?php echo esc_attr($trip->getAvailableFrom() ?: date('Y-m-d')); ?>"
174 data-max-date="<?php echo esc_attr($trip->getAvailableTo() ?: ''); ?>"
175 readonly
176 required>
177 <svg class="yatra-mobile-arrow" fill="none" stroke="currentColor" viewBox="0 0 24 24">
178 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
179 </svg>
180 </div>
181 </div>
182
183 <!-- Travelers Selection -->
184 <div class="yatra-mobile-travelers-section">
185 <?php if ($has_traveler_pricing): ?>
186 <!-- Traveler-Based Pricing -->
187 <div class="yatra-mobile-booking-field">
188 <div class="yatra-mobile-field-icon">
189 <?php echo yatra_svg_icon('users', 'yatra-mobile-icon'); ?>
190 </div>
191 <div class="yatra-mobile-travelers-display" id="mobile-travelers-display">
192 <?php
193 $default_label = null;
194 if (!empty($trip->price_types) && is_array($trip->price_types)) {
195 foreach ($trip->price_types as $pt) {
196 $pt = is_array($pt) ? (object) $pt : $pt;
197 if (!empty($pt->is_default)) {
198 $default_label = !empty($pt->category_label) ? $pt->category_label : null;
199 break;
200 }
201 }
202 if ($default_label === null) {
203 $pt0 = $trip->price_types[0];
204 $pt0 = is_array($pt0) ? (object) $pt0 : $pt0;
205 $default_label = !empty($pt0->category_label) ? $pt0->category_label : null;
206 }
207 }
208 echo esc_html(($default_label ?: __('Traveler', 'yatra')) . ' x 1');
209 ?>
210 </div>
211 </div>
212 <?php else: ?>
213 <!-- Regular Pricing -->
214 <div class="yatra-mobile-booking-field">
215 <div class="yatra-mobile-field-icon">
216 <?php echo yatra_svg_icon('users', 'yatra-mobile-icon'); ?>
217 </div>
218 <div class="yatra-mobile-quantity-controls">
219 <button type="button" class="yatra-mobile-quantity-btn yatra-mobile-quantity-minus" data-target="mobile_num_travelers">
220 <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
221 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 12H4"/>
222 </svg>
223 </button>
224 <input type="number"
225 id="mobile_num_travelers"
226 name="num_travelers"
227 class="yatra-mobile-quantity-input"
228 value="1"
229 min="<?php echo esc_attr($trip->getMinTravelers() ?: 1); ?>"
230 max="<?php echo esc_attr($trip->getMaxTravelers() ?: 20); ?>"
231 readonly
232 data-price="<?php echo esc_attr($base_price); ?>">
233 <button type="button" class="yatra-mobile-quantity-btn yatra-mobile-quantity-plus" data-target="mobile_num_travelers">
234 <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
235 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
236 </svg>
237 </button>
238 </div>
239 </div>
240 <?php endif; ?>
241 </div>
242
243 <!-- Action Buttons -->
244 <div class="yatra-mobile-action-section">
245 <button type="button" class="yatra-mobile-check-btn" id="mobile-check-availability-btn" data-trip-id="<?php echo esc_attr($trip->getId()); ?>">
246 <svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24">
247 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
248 </svg>
249 <span><?php echo esc_html__('Check', 'yatra'); ?></span>
250 </button>
251 <button type="button" class="yatra-mobile-enquire-btn" id="mobile-open-enquiry-modal" onclick="if (window.enquiryModal) { window.enquiryModal.open(); } else if (window.YatraEnquiry) { window.YatraEnquiry.open(); } return false;">
252 <svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24">
253 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z"/>
254 </svg>
255 <span><?php echo esc_html__('Enquire', 'yatra'); ?></span>
256 </button>
257 </div>
258
259 <!-- Close Button -->
260 <div class="yatra-mobile-action-buttons">
261 <button type="button" class="yatra-mobile-close-btn" onclick="yatraCloseMobileSidebar()">
262 <?php echo yatra_svg_icon('x', 'yatra-mobile-close-icon'); ?>
263 </button>
264 </div>
265 </div>
266
267 </div>
268
269 </div>
270
271 <!-- JavaScript for mobile sidebar interactions -->
272 <script>
273 function yatraScrollToBooking() {
274 const bookingForm = document.querySelector('.yatra-booking-form');
275 if (bookingForm) {
276 bookingForm.scrollIntoView({ behavior: 'smooth', block: 'center' });
277 } else {
278 // Fallback to main sidebar
279 const sidebar = document.querySelector('.yatra-trip-sidebar');
280 if (sidebar) {
281 sidebar.scrollIntoView({ behavior: 'smooth', block: 'start' });
282 }
283 }
284 }
285
286 function yatraScrollToContact() {
287 const contactSection = document.querySelector('#contact') || document.querySelector('.yatra-contact-form');
288 if (contactSection) {
289 contactSection.scrollIntoView({ behavior: 'smooth', block: 'center' });
290 }
291 }
292
293
294 function yatraCloseMobileSidebar() {
295 console.log('Close button clicked');
296 const sidebar = document.getElementById('yatra-mobile-sticky-sidebar');
297 if (sidebar) {
298 console.log('Sidebar found, closing...');
299 sidebar.classList.add('user-closed');
300
301 // Save closed state in sessionStorage (persists until page reload)
302 sessionStorage.setItem('yatra-mobile-sidebar-closed', 'true');
303
304 // Hide sidebar with animation
305 setTimeout(() => {
306 sidebar.style.transform = 'translateY(100%)';
307 sidebar.style.opacity = '0';
308 }, 100);
309 } else {
310 console.log('Sidebar not found');
311 }
312 }
313
314 // Auto-show on page load (reset closed state)
315 document.addEventListener('DOMContentLoaded', function() {
316 console.log('DOM loaded, checking mobile sidebar...');
317 const sidebar = document.getElementById('yatra-mobile-sticky-sidebar');
318
319 if (sidebar) {
320 console.log('Mobile sidebar element found');
321
322 // Clear any previous closed state on page load
323 sessionStorage.removeItem('yatra-mobile-sidebar-closed');
324
325 // Ensure sidebar is visible on page load (CSS will handle responsive)
326 sidebar.classList.remove('user-closed');
327 sidebar.classList.remove('hidden');
328
329 console.log('CSS-based responsive behavior initialized');
330 } else {
331 console.log('Mobile sidebar element NOT found');
332 }
333
334 // Initialize mobile booking form functionality
335 initializeMobileBookingForm();
336 });
337
338 function initializeMobileBookingForm() {
339 // Mobile quantity controls
340 const mobileMinusBtns = document.querySelectorAll('.yatra-mobile-quantity-minus');
341 const mobilePlusBtns = document.querySelectorAll('.yatra-mobile-quantity-plus');
342
343 mobileMinusBtns.forEach(btn => {
344 btn.addEventListener('click', function() {
345 const targetId = this.getAttribute('data-target');
346 const input = document.getElementById(targetId);
347 if (input) {
348 const current = parseInt(input.value) || 1;
349 const min = parseInt(input.getAttribute('min')) || 1;
350 if (current > min) {
351 input.value = current - 1;
352 updateMobileTotal();
353 }
354 }
355 });
356 });
357
358 mobilePlusBtns.forEach(btn => {
359 btn.addEventListener('click', function() {
360 const targetId = this.getAttribute('data-target');
361 const input = document.getElementById(targetId);
362 if (input) {
363 const current = parseInt(input.value) || 1;
364 const max = parseInt(input.getAttribute('max')) || 20;
365 if (current < max) {
366 input.value = current + 1;
367 updateMobileTotal();
368 }
369 }
370 });
371 });
372
373 // Mobile date picker - Initialize exactly like main date picker
374 const mobileDateInput = document.getElementById('mobile_travel_date');
375 if (mobileDateInput) {
376 console.log('Initializing mobile date picker...');
377
378 // Check if Flatpickr is available
379 if (typeof flatpickr === 'undefined') {
380 console.log('Flatpickr not loaded, falling back to basic date input');
381 // Make it a regular date input as fallback
382 mobileDateInput.type = 'date';
383 mobileDateInput.removeAttribute('readonly');
384 } else {
385 // Get availability dates from global trip data with fallback
386 let availabilityDates = [];
387 try {
388 if (window.yatraTripData && Array.isArray(window.yatraTripData.availabilityDates)) {
389 availabilityDates = window.yatraTripData.availabilityDates;
390 }
391 } catch (e) {
392 console.log('Error accessing yatraTripData:', e);
393 }
394
395 var mobileFpCommon = {
396 clickOpens: true,
397 appendTo: document.body,
398 static: false,
399 onReady: function(selectedDates, dateStr, instance) {
400 if (instance.calendarContainer) {
401 instance.calendarContainer.style.zIndex = '100050';
402 }
403 },
404 onChange: function(selectedDates, dateStr, instance) {
405 var mainDateInput = document.getElementById('travel_date');
406 if (mainDateInput && mainDateInput._flatpickr) {
407 mainDateInput._flatpickr.setDate(dateStr);
408 }
409 updateMobileTotal();
410 }
411 };
412 function yatraBindMobileDatepickerTouch(inputEl) {
413 if (!inputEl || !inputEl._flatpickr) {
414 return;
415 }
416 var fp = inputEl._flatpickr;
417 var wrap = inputEl.closest('.yatra-mobile-compact-datepicker');
418 if (!wrap) {
419 return;
420 }
421 wrap.addEventListener('click', function(e) {
422 e.stopPropagation();
423 if (!fp.isOpen) {
424 fp.open();
425 }
426 });
427 }
428 // Initialize Flatpickr with same configuration as main date picker
429 var altFormat = (window.yatraTripData && (window.yatraTripData.flatpickrAltFormat || window.yatraTripData.altDateFormat))
430 ? (window.yatraTripData.flatpickrAltFormat || window.yatraTripData.altDateFormat)
431 : null;
432 if (!altFormat && window.yatraTripData && (window.yatraTripData.dateFormat || window.yatraTripData.date_format)) {
433 // Keep in sync with assets/js/trip.js helper (minimal mapping)
434 var php = window.yatraTripData.dateFormat || window.yatraTripData.date_format;
435 var map = { d:'d', j:'j', D:'D', l:'l', m:'m', n:'n', M:'M', F:'F', Y:'Y', y:'y' };
436 altFormat = '';
437 var esc = false;
438 for (var i = 0; i < php.length; i++) {
439 var ch = php[i];
440 if (esc) { altFormat += ch; esc = false; continue; }
441 if (ch === '\\\\') { esc = true; continue; }
442 altFormat += map[ch] || ch;
443 }
444 }
445 if (!altFormat) {
446 altFormat = 'F j, Y';
447 }
448
449 if (availabilityDates.length > 0) {
450 // Availability-based booking - enable only specific dates
451 flatpickr(mobileDateInput, Object.assign({}, mobileFpCommon, {
452 dateFormat: 'Y-m-d',
453 altInput: true,
454 altFormat: altFormat,
455 minDate: 'today',
456 enable: availabilityDates
457 }));
458 } else {
459 // Regular booking - flexible dates with min/max constraints
460 var config = Object.assign({}, mobileFpCommon, {
461 dateFormat: 'Y-m-d',
462 altInput: true,
463 altFormat: altFormat,
464 minDate: 'today'
465 });
466 var minDate = mobileDateInput.getAttribute('data-min-date');
467 var maxDate = mobileDateInput.getAttribute('data-max-date');
468 if (minDate) {
469 config.minDate = minDate;
470 }
471 if (maxDate) {
472 config.maxDate = maxDate;
473 }
474 flatpickr(mobileDateInput, config);
475 }
476 yatraBindMobileDatepickerTouch(mobileDateInput);
477 }
478
479 console.log('Mobile date picker initialized successfully');
480 } else {
481 console.log('Mobile date input not found');
482 }
483
484 // Mobile check availability button
485 const mobileCheckBtn = document.getElementById('mobile-check-availability-btn');
486 if (mobileCheckBtn) {
487 mobileCheckBtn.addEventListener('click', function() {
488 console.log('Mobile Check button clicked');
489 // Sync mobile form data with main form
490 syncMobileToMainForm();
491 // Trigger main check availability
492 const mainCheckBtn = document.getElementById('check-availability-btn');
493 if (mainCheckBtn) {
494 console.log('Triggering main Check button');
495 mainCheckBtn.click();
496 } else {
497 console.log('Main Check button not found');
498 }
499 });
500 } else {
501 console.log('Mobile Check button not found');
502 }
503 }
504
505 function syncMobileToMainForm() {
506 // Sync date
507 const mobileDate = document.getElementById('mobile_travel_date');
508 const mainDate = document.getElementById('travel_date');
509 if (mobileDate && mainDate) {
510 // If main date has Flatpickr instance, use it
511 if (mainDate._flatpickr) {
512 mainDate._flatpickr.setDate(mobileDate.value);
513 } else {
514 mainDate.value = mobileDate.value;
515 }
516 }
517
518 // Sync travelers
519 const mobileTravelers = document.getElementById('mobile_num_travelers');
520 const mainTravelers = document.getElementById('num_travelers');
521 if (mobileTravelers && mainTravelers) {
522 mainTravelers.value = mobileTravelers.value;
523 }
524 }
525
526 function updateMobileTotal() {
527 const sidebar = document.getElementById('yatra-mobile-sticky-sidebar');
528 if (!sidebar) return;
529
530 const pricingType = sidebar.getAttribute('data-pricing-type');
531 const mobileTotal = document.getElementById('mobile-total-amount');
532 if (!mobileTotal) return;
533
534 let total = 0;
535
536 if (pricingType === 'traveler_based') {
537 // Traveler-based pricing: sum all category quantities × prices
538 const travelerInputs = document.querySelectorAll('[id^="mobile_traveler_"]');
539 travelerInputs.forEach(input => {
540 const qty = parseInt(input.value) || 0;
541 const price = parseFloat(input.getAttribute('data-price')) || 0;
542 total += qty * price;
543 });
544 } else {
545 // Regular pricing: simple multiplication
546 const travelersInput = document.getElementById('mobile_num_travelers');
547 if (travelersInput) {
548 const travelers = parseInt(travelersInput.value) || 1;
549 const basePrice = parseFloat(travelersInput.getAttribute('data-price')) || 0;
550 total = travelers * basePrice;
551 }
552 }
553
554 // Format and update display (mobile total element has been removed)
555 // The mobile total price display is no longer shown
556 if (mobileTotal) {
557 if (window.yatraTripData && window.yatraTripData.currencySymbol) {
558 mobileTotal.textContent = window.yatraTripData.currencySymbol + total.toFixed(2);
559 } else {
560 mobileTotal.textContent = formatCurrency(total);
561 }
562 }
563
564 // Also sync with main form total if it exists (main total also removed)
565 const mainTotal = document.getElementById('total-amount');
566 if (mainTotal && window.yatraTripData && window.yatraTripData.currencySymbol) {
567 mainTotal.textContent = window.yatraTripData.currencySymbol + total.toFixed(2);
568 }
569 }
570
571 function formatCurrency(amount) {
572 // Simple currency formatting (can be enhanced)
573 return '$' + amount.toFixed(2);
574 }
575
576 // Auto-hide on scroll down, show on scroll up
577 let lastScrollTop = 0;
578 const mobileSidebar = document.getElementById('yatra-mobile-sticky-sidebar');
579 const scrollThreshold = 100;
580
581 window.addEventListener('scroll', function() {
582 if (!mobileSidebar) return;
583
584 const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
585
586 if (scrollTop > lastScrollTop && scrollTop > scrollThreshold) {
587 // Scrolling down - hide sidebar
588 mobileSidebar.classList.add('hidden');
589 } else {
590 // Scrolling up - show sidebar
591 mobileSidebar.classList.remove('hidden');
592 }
593
594 lastScrollTop = scrollTop <= 0 ? 0 : scrollTop;
595 }, false);
596 </script>
597