PageSourceSearch

https://ethiosearch.com/directory/wp-content/plugins/listeo-core/assets/js/bookings.js?ver=2.0.17

js ethiosearch.com collected 2026-10-03 01:37:02 UTC 92,150 bytes, 2,793 lines download raw bytes

1/* ----------------- Start Document ----------------- */
2(function($){
3"use strict";
4
5$(document).ready(function(){
6  var inputClicked = false;
7  /*----------------------------------------------------*/
8  /*  Booking widget and confirmation form
9	/*----------------------------------------------------*/
10  $("a.booking-confirmation-btn").on("click", function (e) {
11    e.preventDefault();
12    var bookingForm = $("#booking-confirmation");
13    if (bookingForm[0].checkValidity()) {
14      var button = $(this);
15      button.addClass("loading");
16      $("#booking-confirmation").submit();
17    }
18  });
19
20  // reload states based on country
21
22  $("#billing_country").change(function () {
23    var country = $(this).val();
24
25    $.ajax({
26      url: listeo.ajaxurl, // WordPress AJAX
27      type: "POST",
28      data: {
29        action: "listeo_get_booking_states",
30        country: country,
31      },
32      success: function (response) {
33        var select = $("#billing_state");
34        var states = response.data;
35
36        if (states) {
37          select.find("option:not(:first)").remove();
38          $.each(states, function (key, value) {
39            select.append('<option value="' + key + '">' + value + "</option>");
40          });
41        } else {
42          select.find("option:not(:first)").remove();
43          // remove required attribute
44          select.removeAttr("required");
45        }
46      },
47    });
48  });
49  $(document).bind("change", function (e) {
50    if (e.target.checkValidity && !e.target.checkValidity()) {
51      $(e.target).parent().addClass("invalid");
52    } else {
53      $(e.target).parent().removeClass("invalid");
54    }
55  });
56
57  $("#listeo-coupon-link").on("click", function (e) {
58    e.preventDefault();
59    $(".coupon-form").toggle();
60  });
61
62  function validate_coupon(listing_id, price) {
63    var current_codes = $("#coupon_code").val();
64    if (current_codes) {
65      var codes = current_codes.split(",");
66      $.each(codes, function (index, item) {
67        console.log(item);
68        var ajax_data = {
69          listing_id: listing_id,
70          coupon: item,
71          coupons: codes,
72          price: price,
73          action: "listeo_validate_coupon",
74        };
75        $.ajax({
76          type: "POST",
77          dataType: "json",
78          url: listeo.ajaxurl,
79          data: ajax_data,
80
81          success: function (data) {
82            if (data.success) {
83            } else {
84              $("#coupon-widget-wrapper-output div.error")
85                .html(data.message)
86                .show();
87              $(
88                '#coupon-widget-wrapper-applied-coupons span[data-coupon="' +
89                  item +
90                  '"] i'
91              ).trigger("click");
92              $("#apply_new_coupon").val("");
93              $("#coupon-widget-wrapper-output .error").delay(3500).hide(500);
94            }
95            $("a.listeo-booking-widget-apply_new_coupon").removeClass("active");
96          },
97        });
98      });
99    }
100  }
101
102  // Apply new coupon
103  $("a.listeo-booking-widget-apply_new_coupon").on("click", function (e) {
104    e.preventDefault();
105    $(this).addClass("active");
106    $("#coupon-widget-wrapper-output div").hide();
107
108   
109
110    //check if it was already addd
111    var price = $(".booking-estimated-cost").data("price");
112    if (typeof price === "undefined" || price === null) {
113      price = $(".total-costs").data("price");
114    }
115
116    var current_codes = $("#coupon_code").val();
117    var result = current_codes.split(",");
118    var arraycontainscoupon = result.indexOf($("#apply_new_coupon").val()) > -1;
119    var ajax_data = {
120      listing_id: $("#listing_id").val(),
121      coupon: $("#apply_new_coupon").val(),
122      coupons: current_codes,
123      price: price,
124      action: "listeo_validate_coupon",
125
126    };
127    $("#coupon-widget-wrapper-output div").hide();
128    if (arraycontainscoupon) {
129      $(this).removeClass("active");
130      $("input#apply_new_coupon").removeClass("bounce").addClass("bounce");
131      return;
132    }
133    $.ajax({
134      type: "POST",
135      dataType: "json",
136      url: listeo.ajaxurl,
137      data: ajax_data,
138
139      success: function (data) {
140        if (data.success) {
141          if (current_codes.length > 0) {
142            $("#coupon_code").val(current_codes + "," + data.coupon);
143          } else {
144            $("#coupon_code").val(data.coupon);
145          }
146          $("#apply_new_coupon").val("");
147          $("#coupon-widget-wrapper-applied-coupons").append(
148            "<span data-coupon=" +
149              data.coupon +
150              ">" +
151              data.coupon +
152              "<i class='fa fa-times'></i></span>"
153          );
154          $("#coupon-widget-wrapper-output .success").show();
155          if ($("#booking-confirmation-summary").length > 0) {
156            calculate_booking_form_price();
157          } else {
158            if ($("#form-booking").hasClass("form-booking-tickets")) {
159              calculate_price();
160            } else {
161              check_booking();
162            }
163          }
164          $("#coupon-widget-wrapper-output .success").delay(3500).hide(500);
165        } else {
166          $("input#apply_new_coupon").removeClass("bounce").addClass("bounce");
167          $("#coupon-widget-wrapper-output div.error")
168            .html(data.message)
169            .show();
170
171          $("#apply_new_coupon").val("");
172          $("#coupon-widget-wrapper-output .error").delay(3500).hide(500);
173        }
174        $("a.listeo-booking-widget-apply_new_coupon").removeClass("active");
175      },
176    });
177  });
178
179  // Remove coupon from widget and calculate price again
180  $("#coupon-widget-wrapper-applied-coupons").on(
181    "click",
182    "span i",
183    function (e) {
184      var coupon = $(this).parent().data("coupon");
185
186      var coupons = $("#coupon_code").val();
187      var coupons_array = coupons.split(",");
188      coupons_array = coupons_array.filter(function (item) {
189        return item !== coupon;
190      });
191
192      $("#coupon_code").val(coupons_array.join(","));
193      $(this).parent().remove();
194      if ($("#booking-confirmation-summary").length > 0) {
195        calculate_booking_form_price();
196      } else {
197        check_booking();
198        calculate_price();
199      }
200    }
201  );
202
203  //Book now button
204  $(".listing-widget").on("click", "a.book-now", function (e) {
205    var button = $(this);
206
207    if ($("#date-picker").val()) {
208      inputClicked = true;
209
210      if ($(".time-picker").length && !$(".time-picker").val()) {
211        inputClicked = false;
212      }
213      if ($("#slot").length && !$("#slot").val()) {
214        inputClicked = false;
215      }
216      if (inputClicked) {
217        check_booking();
218      }
219    }
220
221    if (inputClicked == false) {
222      $(
223        ".time-picker,.time-slots-dropdown,.date-picker-listing-date_range"
224      ).addClass("bounce");
225    } else {
226      button.addClass("loading");
227    }
228    e.preventDefault();
229
230    var freeplaces = button.data("freeplaces");
231
232    // var checked = $(".bookable-services input[type=checkbox]:checked").length;
233
234    // if (!checked) {
235    // 	alert("You must select at least one extra service.");
236    // 	return false;
237    // }
238
239    setTimeout(function () {
240      button.removeClass("loading");
241      $(
242        ".time-picker,.time-slots-dropdown,.date-picker-listing-date_range"
243      ).removeClass("bounce");
244    }, 3000);
245
246    try {
247      // Calculate total guests
248      var adults = parseInt($(".adults").val()) || 0;
249      var children = parseInt($("input[name=childrenQtyInput]").val()) || 0;
250      var infants = parseInt($("input[name=infantsQtyInput]").val()) || 0;
251      var animals = parseInt($("input[name=animalsQtyInput]").val()) || 0;
252      
253      // Get max guests from the form
254      var maxGuests = parseInt($(".adults").data("max"));
255      
256      // Calculate total counting guests (adults + children count towards max, infants and animals don't)
257      var totalCountingGuests = adults + children;
258      
259      if (totalCountingGuests > maxGuests) {
260        $("#booking-widget-message").show().html(listeo_core.exceed_guests_limit);
261        return false;
262      }
263
264      if (freeplaces > 0) {
265        // preparing data for ajax to send to booking form
266
267        if ($("#date-picker").data("rental-timepicker")) {
268          // var startDataSQL need to have hour
269          var startDataSql = moment(
270            $("#date-picker").data("daterangepicker").startDate,
271            ["MM/DD/YYYY hh:mm A"]
272          ).format("YYYY-MM-DD HH:mm:ss");
273          var endDataSql = moment(
274            $("#date-picker").data("daterangepicker").endDate,
275            ["MM/DD/YYYY hh:mm A"]
276          ).format("YYYY-MM-DD HH:mm:ss");
277        } else {
278          var startDataSql = moment(
279            $("#date-picker").data("daterangepicker").startDate,
280            ["MM/DD/YYYY"]
281          ).format("YYYY-MM-DD");
282          var endDataSql = moment(
283            $("#date-picker").data("daterangepicker").endDate,
284            ["MM/DD/YYYY"]
285          ).format("YYYY-MM-DD");
286        }
287
288        var ajax_data = {
289          listing_type: $("#listing_type").val(),
290          listing_id: $("#listing_id").val(),
291          //'nonce': nonce
292        };
293        var invalid = false;
294        if (startDataSql) ajax_data.date_start = startDataSql;
295        if (endDataSql) ajax_data.date_end = endDataSql;
296        if ($("input#slot").val()) ajax_data.slot = $("input#slot").val();
297        if ($(".time-picker#_hour").val())
298          ajax_data._hour = $(".time-picker#_hour").val();
299        if ($(".time-picker#_hour_end").val())
300          ajax_data._hour_end = $(".time-picker#_hour_end").val();
301        if ($(".adults").val()) ajax_data.adults = $(".adults").val();
302        if ($("input[name=childrenQtyInput]").val()) ajax_data.children = $("input[name=childrenQtyInput]").val();
303        if ($("input[name=infantsQtyInput]").val()) ajax_data.infants = $("input[name=infantsQtyInput]").val();
304        if ($("input[name=animalsQtyInput]").val()) ajax_data.animals = $("input[name=animalsQtyInput]").val();
305        if ($("#tickets").val()) ajax_data.tickets = $("#tickets").val();
306        if ($("#coupon_code").val()) ajax_data.coupon = $("#coupon_code").val();
307
308        // Check if listing type supports time slots
309        var listing_type = $("#listing_type").val();
310        var type_config = listeo_core.listing_types_config && listeo_core.listing_types_config[listing_type];
311        var supports_time_slots = type_config ? type_config.supports_time_slots : (listing_type == "single_day"); // fallback
312        
313        if (supports_time_slots) {
314          if (
315            $("input#slot").val() == undefined ||
316            $("input#slot").val() == ""
317          ) {
318            inputClicked = false;
319            invalid = true;
320          }
321          if ($(".time-picker").length) {
322            invalid = false;
323          }
324        }
325
326        if (invalid == false) {
327          var services = [];
328          // $.each($("input[name='_service[]']:checked"), function(){
329          //           		services.push($(this).val());
330          //       		});
331          $.each($("input.bookable-service-checkbox:checked"), function () {
332            var quantity = $(this)
333              .parent()
334              .find("input.bookable-service-quantity")
335              .val();
336            services.push({ service: $(this).val(), value: quantity });
337          });
338          ajax_data.services = services;
339          $("input#booking").val(JSON.stringify(ajax_data));
340       
341          $("#form-booking").submit();
342        }
343      }
344    } catch (e) {
345      console.log(e);
346    }
347
348    // Check if listing type is event-based (no time slots support)
349    var listing_type = $("#listing_type").val();
350    var type_config = listeo_core.listing_types_config && listeo_core.listing_types_config[listing_type];
351    var is_event_type = type_config ? !type_config.supports_time_slots : (listing_type == "event"); // fallback
352    
353    if (is_event_type) {
354      var ajax_data = {
355        listing_type: $("#listing_type").val(),
356        listing_id: $("#listing_id").val(),
357        date_start: $(".booking-event-date span").html(),
358        date_end: $(".booking-event-date span").html(),
359        //'nonce': nonce
360      };
361      if ($("#coupon_code").val()) ajax_data.coupon = $("#coupon_code").val();
362      var services = [];
363      $.each($("input.bookable-service-checkbox:checked"), function () {
364        var quantity = $(this)
365          .parent()
366          .find("input.bookable-service-quantity")
367          .val();
368        services.push({ service: $(this).val(), value: quantity });
369      });
370      ajax_data.services = services;
371
372      // converent data
373      ajax_data["date_start"] = moment(
374        ajax_data["date_start"],
375        wordpress_date_format.date
376      ).format("YYYY-MM-DD");
377      ajax_data["date_end"] = moment(
378        ajax_data["date_end"],
379        wordpress_date_format.date
380      ).format("YYYY-MM-DD");
381      if ($("#tickets").val()) ajax_data.tickets = $("#tickets").val();
382      $("input#booking").val(JSON.stringify(ajax_data));
383
384      $("#form-booking").submit();
385    }
386  });
387
388  if (Boolean(listeo_core.clockformat)) {
389    var dateformat_even = wordpress_date_format.date + " HH:mm";
390  } else {
391    var dateformat_even = wordpress_date_format.date + " hh:mm A";
392  }
393
394  function updateCounter() {
395    var len = $(".bookable-services input[type='checkbox']:checked").length;
396    if (len > 0) {
397      $(".booking-services span.services-counter").text("" + len + "");
398      $(".booking-services span.services-counter").addClass("counter-visible");
399    } else {
400      $(".booking-services span.services-counter").removeClass(
401        "counter-visible"
402      );
403      $(".booking-services span.services-counter").text("0");
404    }
405  }
406
407  $(".single-service").on("click", function () {
408    updateCounter();
409    $(".booking-services span.services-counter").addClass("rotate-x");
410
411    setTimeout(function () {
412      $(".booking-services span.services-counter").removeClass("rotate-x");
413    }, 300);
414  });
415
416  // $( ".input-datetime" ).each(function( index ) {
417  // 	var $this = $(this);
418  // 	var input = $(this).next('input');
419  //   	var date =  parseInt(input.val());
420  //   	if(date){
421  // 	  	var a = new Date(date);
422  // 		var timestamp = moment(a);
423  // 		$this.val(timestamp.format(dateformat_even));
424  //   	}
425
426  // });
427
428  //$('#_event_date').val(timestamp.format(dateformat_even));
429
430  // there are two fields with id _event_date and _event_date_end, make sure the event_date_end is not before event_date
431  // $('#_event_date_end').on('change', function() {
432  // 	alert('test');
433  // 	var start_date = $('#_event_date').val();
434  // 	var end_date = $('#_event_date_end').val();
435  // 	if(end_date < start_date){
436  // 		$('#_event_date_end').val(start_date);
437  // 	}
438  // });
439window.initializeDateRangePicker = function ($context = $(document)) {
440  $context.find(".input-datetime").each(function () {
441    // Prevent double init
442    if (!$(this).data("daterangepicker")) {
443      $(this).daterangepicker({
444        opens: "left",
445        singleDatePicker: true,
446        timePicker: true,
447        autoUpdateInput: false,
448        timePicker24Hour: Boolean(listeo_core.clockformat),
449        minDate: moment().subtract(0, "days"),
450        locale: {
451          format: dateformat_even,
452          firstDay: parseInt(wordpress_date_format.day),
453          applyLabel: listeo_core.applyLabel,
454          cancelLabel: listeo_core.cancelLabel,
455          fromLabel: listeo_core.fromLabel,
456          toLabel: listeo_core.toLabel,
457          customRangeLabel: listeo_core.customRangeLabel,
458          daysOfWeek: [
459            listeo_core.day_short_su,
460            listeo_core.day_short_mo,
461            listeo_core.day_short_tu,
462            listeo_core.day_short_we,
463            listeo_core.day_short_th,
464            listeo_core.day_short_fr,
465            listeo_core.day_short_sa,
466          ],
467          monthNames: [
468            listeo_core.january,
469            listeo_core.february,
470            listeo_core.march,
471            listeo_core.april,
472            listeo_core.may,
473            listeo_core.june,
474            listeo_core.july,
475            listeo_core.august,
476            listeo_core.september,
477            listeo_core.october,
478            listeo_core.november,
479            listeo_core.december,
480          ],
481        },
482      });
483    }
484  });
485};
486initializeDateRangePicker();
487
488  $(".input-datetime").on("apply.daterangepicker", function (ev, picker) {
489    $(this).val(picker.startDate.format(dateformat_even));
490    var end_date = $("#_event_date_end").length;
491    if (end_date) {
492      $(".input-datetime#_event_date_end")
493        .data("daterangepicker")
494        .setMinDate(picker.startDate.format(dateformat_even));
495    }
496  });
497
498  $(".input-datetime").on("cancel.daterangepicker", function (ev, picker) {
499    $(this).val("");
500  });
501
502  $(".input-date").daterangepicker({
503    opens: "left",
504    // checking attribute listing type and set type of calendar
505    singleDatePicker: true,
506    timePicker: false,
507    autoUpdateInput: false,
508
509    minDate: moment().subtract(0, "days"),
510
511    locale: {
512      format: dateformat_even,
513      firstDay: parseInt(wordpress_date_format.day),
514      applyLabel: listeo_core.applyLabel,
515      cancelLabel: listeo_core.cancelLabel,
516      fromLabel: listeo_core.fromLabel,
517      toLabel: listeo_core.toLabel,
518      customRangeLabel: listeo_core.customRangeLabel,
519      daysOfWeek: [
520        listeo_core.day_short_su,
521        listeo_core.day_short_mo,
522        listeo_core.day_short_tu,
523        listeo_core.day_short_we,
524        listeo_core.day_short_th,
525        listeo_core.day_short_fr,
526        listeo_core.day_short_sa,
527      ],
528      monthNames: [
529        listeo_core.january,
530        listeo_core.february,
531        listeo_core.march,
532        listeo_core.april,
533        listeo_core.may,
534        listeo_core.june,
535        listeo_core.july,
536        listeo_core.august,
537        listeo_core.september,
538        listeo_core.october,
539        listeo_core.november,
540        listeo_core.december,
541      ],
542    },
543  });
544
545  $(".input-date").on("apply.daterangepicker", function (ev, picker) {
546    $(this).val(picker.startDate.format("YYYY-MM-DD"));
547  });
548
549  $(".input-date").on("cancel.daterangepicker", function (ev, picker) {
550    $(this).val("");
551  });
552  // $('.input-datetime').on( 'apply.daterangepicker', function(){
553
554  // 	var picked_date = $(this).val();
555  // 	var input = $(this).next('input');
556  // 	input.val(moment(picked_date,dateformat_even).format('YYYY-MM-DD HH:MM:SS'));
557  // } );
558
559  function wpkGetThisDateSlots(date) {
560    var slots = {
561      isFirstSlotTaken: false,
562      isSecondSlotTaken: false,
563    };
564
565    // Check if listing type supports time slots
566    var listing_type = $("#listing_type").val();
567    var type_config = listeo_core.listing_types_config && listeo_core.listing_types_config[listing_type];
568    var supports_time_slots = type_config ? type_config.supports_time_slots : (listing_type != "event"); // fallback
569    
570    if (!supports_time_slots) return slots;
571
572    var dateString = date.format("YYYY-MM-DD");
573
574    // Check disabled dates first
575    if (typeof disabledDates !== "undefined") {
576      if (wpkIsDateInArray(date, disabledDates)) {
577        slots.isFirstSlotTaken = slots.isSecondSlotTaken = true;
578        console.log("Date " + dateString + " is in disabled dates");
579        return slots;
580      }
581    }
582
583    // Check booking dates
584    if (typeof wpkStartDates != "undefined" && typeof wpkEndDates != "undefined") {
585      slots.isSecondSlotTaken = wpkIsDateInArray(date, wpkStartDates);
586      slots.isFirstSlotTaken = wpkIsDateInArray(date, wpkEndDates);
587      
588      // Debug logging
589   
590    }
591
592    return slots;
593  }
594
595  function wpkIsDateInArray(date, array) {
596    var dateString = date.format("YYYY-MM-DD");
597    var isInArray = false;
598    
599    // Check if array is object (with numeric keys) or regular array
600    if (Array.isArray(array)) {
601      isInArray = array.indexOf(dateString) !== -1;
602    } else if (typeof array === 'object') {
603      // Convert object values to array and check
604      var arrayValues = Object.values(array);
605      isInArray = arrayValues.indexOf(dateString) !== -1;
606    }
607    
608    
609    return isInArray;
610  }
611
612  let startDate = Cookies.get("listeo_rental_startdate");
613  let endDate = Cookies.get("listeo_rental_enddate");
614  let minSpan = $("#date-picker").data("minspan");
615  let maxSpan = $("#date-picker").data("maxspan");
616  let availableHours = [];
617  var is24Hour = Boolean(listeo_core.clockformat);
618  var timePickerIncrement = $("#date-picker").data("time-increment");
619  //format: "M/DD hh:mm A";
620
621  // if startDate exist and is not empty, set autoUpdateInput to true
622  let autoUpdateInput = true;
623  if(startDate === undefined) {
624    autoUpdateInput = false;
625  }
626
627  // Update the daterangepicker options to properly handle blocked dates
628  let daterangepicker_options = {
629    opens: "left",
630    autoUpdateInput: autoUpdateInput,
631    singleDatePicker: function() {
632      var listing_type = $("#date-picker").data("listing_type");
633      var type_config = listeo_core.listing_types_config && listeo_core.listing_types_config[listing_type];
634      // Use single date picker for non-rental types (events, services)
635      return type_config
636        ? type_config.supports_time_slots
637        : listing_type != "date_range";
638    }(),
639    timePicker: $("#date-picker").data("rental-timepicker") == true ? true : false,
640    minDate: moment().subtract(0, "days"),
641    timePicker24Hour: is24Hour,
642    timePickerIncrement: timePickerIncrement,
643    minSpan: { days: minSpan },
644    startDate: startDate ? startDate : moment(),
645    endDate: endDate ? endDate : moment().add(minSpan, "days"),
646    locale: {
647      format: $("#date-picker").data("rental-timepicker") == true
648        ? wordpress_date_format.date + " hh:mm A"
649        : wordpress_date_format.date,
650      firstDay: parseInt(wordpress_date_format.day),
651      applyLabel: listeo_core.applyLabel,
652      cancelLabel: listeo_core.cancelLabel,
653      fromLabel: listeo_core.fromLabel,
654      toLabel: listeo_core.toLabel,
655      customRangeLabel: listeo_core.customRangeLabel,
656      daysOfWeek: [
657        listeo_core.day_short_su,
658        listeo_core.day_short_mo,
659        listeo_core.day_short_tu,
660        listeo_core.day_short_we,
661        listeo_core.day_short_th,
662        listeo_core.day_short_fr,
663        listeo_core.day_short_sa,
664      ],
665      monthNames: [
666        listeo_core.january,
667        listeo_core.february,
668        listeo_core.march,
669        listeo_core.april,
670        listeo_core.may,
671        listeo_core.june,
672        listeo_core.july,
673        listeo_core.august,
674        listeo_core.september,
675        listeo_core.october,
676        listeo_core.november,
677        listeo_core.december,
678      ],
679    },
680
681    isCustomDate: function (date) {
682      if ($("#date-picker").data("rental-timepicker") == true) {
683        var dateString = date.format("YYYY-MM-DD");
684
685        if (partialBookedDates && partialBookedDates[dateString]) {
686          var isFullyBooked = isDateFullyBooked(partialBookedDates[dateString]);
687          if (isFullyBooked) {
688            return "fully-booked";
689          }
690          return "partially-booked";
691        }
692
693        return "available";
694      } else {
695        var slots = wpkGetThisDateSlots(date);
696    
697        if (!slots.isFirstSlotTaken && !slots.isSecondSlotTaken) return [];
698
699        if (slots.isFirstSlotTaken && !slots.isSecondSlotTaken) {
700          return ["first-slot-taken"];
701        }
702
703        if (slots.isSecondSlotTaken && !slots.isFirstSlotTaken) {
704          return ["second-slot-taken"];
705        }
706
707        // If both slots are taken, return fully booked class
708        if (slots.isFirstSlotTaken && slots.isSecondSlotTaken) {
709          return ["fully-booked"];
710        }
711      }
712    },
713
714    isInvalidDate: function (date) {
715      // For event-based types, don't block any dates
716      var listing_type = $("#listing_type").val();
717      var type_config = listeo_core.listing_types_config && listeo_core.listing_types_config[listing_type];
718      var is_event_type = type_config ? !type_config.supports_time_slots : (listing_type == "event"); // fallback
719      
720      if (is_event_type) return false;
721      
722      // For services with disabled dates
723      if ($("#listing_type").val() == "single_day" && typeof disabledDates !== "undefined") {
724        if (jQuery.inArray(date.format("YYYY-MM-DD"), disabledDates) !== -1) {
725          return true;
726        }
727      }
728
729      // Handle time slots for services
730      if ($(".time-slots-dropdown").length) {
731        var timeslotdays = $(".time-slots-dropdown").data("slots-days").toString();
732        var timeslotdaysArray = timeslotdays.split(",");
733        
734        if (timeslotdaysArray.includes(moment(date).day().toString())) {
735          return false;
736        } else {
737          return true;
738        }
739      }
740
741      // For rental listings
742      if ($("#listing_type").val() == "date_range") {
743        if ($("#date-picker").data("rental-timepicker") == true) {
744          var dateString = date.format("YYYY-MM-DD");
745
746          // Check disabled dates
747          if (
748            typeof disabledDates !== "undefined" &&
749            disabledDates.indexOf(dateString) > -1
750          ) {
751            return true;
752          }
753
754          return false;
755        } else {
756          // Use the wpkGetThisDateSlots function to determine if date should be blocked
757          var slots = wpkGetThisDateSlots(date);
758
759          // Block dates where both slots are taken (fully booked)
760          return slots.isFirstSlotTaken && slots.isSecondSlotTaken;
761        }
762      }
763
764      return false;
765    },
766  };
767
768  $("#date-picker").daterangepicker(daterangepicker_options);
769
770  $("#date-picker").on("show.daterangepicker", function (ev, picker) {
771    $(".daterangepicker").addClass("calendar-visible calendar-animated");
772    $(".daterangepicker").removeClass("calendar-hidden");
773  });
774  $("#date-picker").on("hide.daterangepicker", function (ev, picker) {
775    $(".daterangepicker").removeClass("calendar-visible");
776    $(".daterangepicker").addClass("calendar-hidden");
777  });
778
779  // Handle date/time selection
780  $(".date-picker-listing-date_range").on(
781    "showCalendar.daterangepicker",
782    function (ev, picker) {
783      // Force update time picker whenever calendar is shown
784      if ($("#date-picker").data("rental-timepicker") == true) {
785        setTimeout(function () {
786          updateTimePicker(picker);
787        }, 50);
788      }
789    }
790  );
791
792  // Handle time picker changes
793  $(".date-picker-listing-date_range").on(
794    "show.daterangepicker",
795    function (ev, picker) {
796      if ($("#date-picker").data("rental-timepicker") == true) {
797        // Initial time picker update
798        updateTimePicker(picker);
799
800        // Monitor for any changes to the time pickers
801        $(picker.container)
802          .find(".calendar-time")
803          .on("mousedown", function () {
804            //   setTimeout(function () {
805            updateTimePicker(picker);
806            // }, 50);
807          });
808      }
809    }
810  );
811
812  $(".date-picker-listing-date_range").on(
813    "show.daterangepicker",
814    function (ev, picker) {
815      if ($("#date-picker").data("rental-timepicker") == true) {
816        setTimeout(function () {
817          // Add labels to calendar times if they don't exist
818          picker.container.find(".calendar-time").each(function (index) {
819            var $timeContainer = $(this);
820            if (!$timeContainer.prev().hasClass("calendar-time-label")) {
821              var label =
822                index === 0
823                  ? listeo_core.start_time_label
824                  : listeo_core.end_time_label;
825              $timeContainer.before(
826                '<div class="calendar-time-label">' + label + "</div>"
827              );
828            }
829          });
830
831          // Wrap selects in custom wrapper
832        }, 0);
833      }
834    }
835  );
836
837  function updateTimePicker(picker) {
838    var startDate = picker.startDate.format("YYYY-MM-DD");
839    var endDate = picker.endDate.format("YYYY-MM-DD");
840
841    // Get available times for start and end dates
842    var startTimes = getAvailableTimes(
843      startDate,
844      partialBookedDates[startDate],
845      "check-in"
846    );
847    var endTimes = getAvailableTimes(
848      endDate,
849      partialBookedDates[endDate],
850      "check-out"
851    );
852
853    // Get the time selectors for both start and end
854    var startContainer = $(picker.container).find(".calendar-time").first();
855    var endContainer = $(picker.container).find(".calendar-time").last();
856
857    // Update start time selectors
858    updateTimeSelectors(startContainer, startTimes, picker.startDate);
859
860    // Update end time selectors
861    updateTimeSelectors(endContainer, endTimes, picker.endDate);
862  }
863
864  function updateTimeSelectors(container, availableTimes, currentDate) {
865    var hourSelect = container.find(".hourselect");
866    var minuteSelect = container.find(".minuteselect");
867
868    // Get unique available hours
869    var availableHours = [
870      ...new Set(availableTimes.map((time) => parseInt(time.split(":")[0]))),
871    ].sort((a, b) => a - b);
872
873    // Clear and populate hour select
874    hourSelect.empty();
875    availableHours.forEach((hour) => {
876      hourSelect.append(
877        $("<option>", {
878          value: hour,
879          text: ("0" + hour).slice(-2),
880        })
881      );
882    });
883
884    // If current hour is not in available hours, select first available
885    var currentHour = currentDate.hour();
886    if (!availableHours.includes(currentHour)) {
887      hourSelect.val(availableHours[0]);
888      currentDate.hour(availableHours[0]);
889    } else {
890      hourSelect.val(currentHour);
891    }
892
893    // Update minutes for selected hour
894    updateMinutes(hourSelect, minuteSelect, availableTimes, currentDate);
895
896    // Add change handler for hours
897    hourSelect.off("change").on("change", function () {
898      updateMinutes(hourSelect, minuteSelect, availableTimes, currentDate);
899    });
900  }
901
902  function updateMinutes(
903    hourSelect,
904    minuteSelect,
905    availableTimes,
906    currentDate
907  ) {
908    var selectedHour = parseInt(hourSelect.val());
909
910    // Get available minutes for selected hour
911    var availableMinutes = availableTimes
912      .filter((time) => parseInt(time.split(":")[0]) === selectedHour)
913      .map((time) => parseInt(time.split(":")[1]))
914      .sort((a, b) => a - b);
915
916    // Clear and populate minute select
917    minuteSelect.empty();
918    availableMinutes.forEach((minute) => {
919      minuteSelect.append(
920        $("<option>", {
921          value: minute,
922          text: ("0" + minute).slice(-2),
923        })
924      );
925    });
926
927    // If current minute is not in available minutes, select first available
928    var currentMinute = currentDate.minute();
929    if (!availableMinutes.includes(currentMinute)) {
930      minuteSelect.val(availableMinutes[0]);
931      currentDate.minute(availableMinutes[0]);
932    } else {
933      minuteSelect.val(currentMinute);
934    }
935  }
936
937  function getAvailableTimes(date, bookedTimes, type) {
938    var times = [];
939    var startHour = type === "check-in" ? 7 : 10; // Check-in starts at 9 AM
940    var endHour = type === "check-out" ? 20 : 17; // Check-out until 5 PM
941
942    // Generate times in 15-minute increments
943    for (var hour = startHour; hour <= endHour; hour++) {
944      for (var minute = 0; minute < 60; minute += timePickerIncrement) {
945        var timeStr = ("0" + hour).slice(-2) + ":" + ("0" + minute).slice(-2);
946        if (!isTimeBooked(timeStr, bookedTimes)) {
947          times.push(timeStr);
948        }
949      }
950    }
951
952    return times;
953  }
954
955  function isTimeBooked(timeStr, bookedTimes) {
956    if (!bookedTimes) return false;
957
958    return bookedTimes.some(function (booking) {
959      return (
960        timeToMinutes(timeStr) >= timeToMinutes(booking.start) &&
961        timeToMinutes(timeStr) <= timeToMinutes(booking.end)
962      );
963    });
964  }
965
966  function isDateFullyBooked(bookings) {
967    var timeBlocks = mergeTimeBlocks(bookings);
968    return timeBlocks.some(function (block) {
969      return block.start === "00:00" && block.end === "23:59";
970    });
971  }
972
973  function mergeTimeBlocks(bookings) {
974    if (!bookings || !bookings.length) return [];
975
976    var sorted = bookings.sort(function (a, b) {
977      return a.start.localeCompare(b.start);
978    });
979
980    var merged = [];
981    var current = sorted[0];
982
983    for (var i = 1; i < sorted.length; i++) {
984      if (timeToMinutes(current.end) >= timeToMinutes(sorted[i].start)) {
985        if (timeToMinutes(sorted[i].end) > timeToMinutes(current.end)) {
986          current.end = sorted[i].end;
987        }
988      } else {
989        merged.push(current);
990        current = sorted[i];
991      }
992    }
993    merged.push(current);
994
995    return merged;
996  }
997
998  function timeToMinutes(timeStr) {
999    var parts = timeStr.split(":");
1000    return parseInt(parts[0]) * 60 + parseInt(parts[1]);
1001  }
1002
1003  $(".date-picker-listing-date_range").on(
1004    "apply.daterangepicker",
1005    function (ev, picker) {
1006      if ($("#date-picker").data("rental-timepicker") == true) {
1007        if (picker.startDate && picker.endDate) {
1008          var timeFormat = is24Hour ? "HH:mm" : "hh:mm A";
1009          var startFormatted =
1010            picker.startDate.format(wordpress_date_format.date) +
1011            " " +
1012            picker.startDate.format(timeFormat);
1013          var endFormatted =
1014            picker.endDate.format(wordpress_date_format.date) +
1015            " " +
1016            picker.endDate.format(timeFormat);
1017          $(this).val(startFormatted + " - " + endFormatted);
1018        }
1019      } else {
1020        $(this).val(
1021          picker.startDate.format(wordpress_date_format.date) +
1022            " - " +
1023            picker.endDate.format(wordpress_date_format.date)
1024        );
1025      }
1026      // if it's other listing type, set date format 
1027    }
1028  );
1029
1030  $(".date-picker-listing-single_day").on(
1031    "apply.daterangepicker",
1032    function (ev, picker) {
1033      // For services we typically want just the selected date
1034      $(this).val(picker.startDate.format(wordpress_date_format.date));
1035
1036   
1037    }
1038  );
1039
1040  function updateTimePicker(picker) {
1041    var startDate = picker.startDate.format("YYYY-MM-DD");
1042    var endDate = picker.endDate.format("YYYY-MM-DD");
1043
1044    var startTimes = getAvailableTimes(
1045      startDate,
1046      partialBookedDates[startDate],
1047      "check-in"
1048    );
1049    var endTimes = getAvailableTimes(
1050      endDate,
1051      partialBookedDates[endDate],
1052      "check-out"
1053    );
1054
1055    var startContainer = $(picker.container).find(".calendar-time").first();
1056    var endContainer = $(picker.container).find(".calendar-time").last();
1057
1058    updateTimeSelectors(startContainer, startTimes, picker.startDate);
1059    updateTimeSelectors(endContainer, endTimes, picker.endDate);
1060  }
1061
1062  function updateTimeSelectors(container, availableTimes, currentDate) {
1063    var is24Hour = Boolean(listeo_core.clockformat);
1064    var hourSelect = container.find(".hourselect");
1065    var minuteSelect = container.find(".minuteselect");
1066    var ampmSelect = container.find(".ampmselect");
1067
1068    // Get current state
1069    var currentHour24 = currentDate.hour();
1070    var currentMinute = currentDate.minute();
1071    var isPM = currentHour24 >= 12;
1072
1073    function updateHourOptions() {
1074      var availableHours24 = [
1075        ...new Set(availableTimes.map((time) => parseInt(time.split(":")[0]))),
1076      ].sort((a, b) => a - b);
1077
1078      // Filter hours based on AM/PM if in 12-hour mode
1079      if (!is24Hour) {
1080        var selectedIsPM = ampmSelect.val() === "PM";
1081        availableHours24 = availableHours24.filter((hour) => {
1082          return selectedIsPM ? hour >= 12 : hour < 12;
1083        });
1084      }
1085
1086      hourSelect.empty();
1087      availableHours24.forEach((hour24) => {
1088        var displayHour = is24Hour ? hour24 : hour24 % 12 || 12;
1089        hourSelect.append(
1090          $("<option>", {
1091            value: hour24, // Store 24-hour value
1092            text: ("0" + displayHour).slice(-2),
1093          })
1094        );
1095      });
1096
1097      // Set appropriate hour
1098      if (availableHours24.length > 0) {
1099        var currentHourExists = availableHours24.includes(currentHour24);
1100        var hourToSet = currentHourExists ? currentHour24 : availableHours24[0];
1101        hourSelect.val(hourToSet);
1102        currentDate.hour(hourToSet);
1103        return hourToSet;
1104      }
1105      return null;
1106    }
1107
1108    // Initial hour update
1109    var selectedHour24 = updateHourOptions();
1110
1111    // Update minutes for the selected hour
1112    function updateMinutes(hour24) {
1113      if (hour24 === null) return;
1114
1115      var availableMinutes = availableTimes
1116        .filter((time) => parseInt(time.split(":")[0]) === hour24)
1117        .map((time) => parseInt(time.split(":")[1]))
1118        .sort((a, b) => a - b);
1119
1120      minuteSelect.empty();
1121      availableMinutes.forEach((minute) => {
1122        minuteSelect.append(
1123          $("<option>", {
1124            value: minute,
1125            text: ("0" + minute).slice(-2),
1126          })
1127        );
1128      });
1129
1130      // Set appropriate minute
1131      if (availableMinutes.length > 0) {
1132        if (availableMinutes.includes(currentMinute)) {
1133          minuteSelect.val(currentMinute);
1134        } else {
1135          minuteSelect.val(availableMinutes[0]);
1136          currentDate.minute(availableMinutes[0]);
1137        }
1138      }
1139    }
1140
1141    updateMinutes(selectedHour24);
1142
1143    // Event handlers
1144    if (!is24Hour) {
1145      ampmSelect.off("change").on("change", function () {
1146        var newHour24 = updateHourOptions();
1147        updateMinutes(newHour24);
1148      });
1149    }
1150
1151    hourSelect.off("change").on("change", function () {
1152      var selectedHour24 = parseInt($(this).val());
1153      currentDate.hour(selectedHour24);
1154      updateMinutes(selectedHour24);
1155    });
1156
1157    minuteSelect.off("change").on("change", function () {
1158      currentDate.minute(parseInt($(this).val()));
1159    });
1160
1161    // Set initial AM/PM if in 12-hour mode
1162    if (!is24Hour) {
1163      ampmSelect.val(isPM ? "PM" : "AM");
1164    }
1165  }
1166
1167  // Utility functions remain the same...
1168  function getAvailableTimes(date, bookedTimes, type) {
1169    var times = [];
1170    //if availableDays is not set, return empty array
1171    console.log(availableDays);
1172    if (
1173      typeof availableDays === "undefined" ||
1174      availableDays === null ||
1175      availableDays === ""
1176    ) {
1177      var listingOpeningHours = [];
1178    } else {
1179      var listingOpeningHours = JSON.parse(availableDays);
1180    }
1181
1182    // Get day of week from the date (0 = Monday, 6 = Sunday)
1183    // var dayIndex = moment(date).day();
1184    // dayIndex = dayIndex === 0 ? 6 : dayIndex - 1;
1185    var dayOfWeek = moment(date).isoWeekday(); // Returns 1-7 (Monday-Sunday)
1186
1187    // Get opening hours for this day
1188    var dayIndex = dayOfWeek - 1;
1189    // Get opening hours for this day
1190    var dayHours = listingOpeningHours[dayIndex];
1191    console.log(dayHours);
1192    // If no hours set for this day or empty values, use default full day availability
1193    if (
1194      !dayHours ||
1195      !dayHours.opening ||
1196      !dayHours.closing ||
1197      dayHours.opening[0] === "" ||
1198      dayHours.closing[0] === ""
1199    ) {
1200      // Use default hours based on check-in/check-out type
1201      // var defaultHours = {
1202      //     opening: [(type === 'check-in' ? "09:00" : "10:00")],
1203      //     closing: [(type === 'check-out' ? "20:00" : "17:00")]
1204      // };
1205
1206      //dayHours = defaultHours;
1207      dayHours = {
1208        opening: ["00:00"],
1209        closing: ["23:59"],
1210      };
1211    }
1212
1213    // Process each set of opening/closing hours
1214    for (var i = 0; i < dayHours.opening.length; i++) {
1215      var openTime = dayHours.opening[i];
1216      var closeTime = dayHours.closing[i];
1217
1218      if (!openTime || !closeTime) continue;
1219
1220      // Convert opening/closing times to hours and minutes
1221      var openHour = parseInt(openTime.split(":")[0]);
1222      var closeHour = parseInt(closeTime.split(":")[0]);
1223      var openMinute = parseInt(openTime.split(":")[1]);
1224      var closeMinute = parseInt(closeTime.split(":")[1]);
1225
1226      // // Apply check-in/check-out restrictions
1227      // if (type === 'check-in') {
1228      //     // Don't allow check-in less than 1 hour before closing
1229      //     var closeTimeInMinutes = closeHour * 60 + closeMinute;
1230      //     closeTimeInMinutes -= 60; // Subtract 1 hour
1231      //     closeHour = Math.floor(closeTimeInMinutes / 60);
1232      //     closeMinute = closeTimeInMinutes % 60;
1233      // } else { // check-out
1234      //     // Don't allow check-out before 10 AM or after 5 PM
1235      //     if (openHour < 10) {
1236      //         openHour = 10;
1237      //         openMinute = 0;
1238      //     }
1239      //     if (closeHour > 17 || (closeHour === 17 && closeMinute > 0)) {
1240      //         closeHour = 17;
1241      //         closeMinute = 0;
1242      //     }
1243      // }
1244
1245      // Generate available times within the valid range
1246      var currentHour = openHour;
1247      var currentMinute =
1248        currentHour === openHour
1249          ? Math.ceil(openMinute / timePickerIncrement) * timePickerIncrement
1250          : 0;
1251
1252      var endTimeInMinutes = closeHour * 60 + closeMinute;
1253
1254      while (currentHour * 60 + currentMinute <= endTimeInMinutes) {
1255        var timeStr =
1256          ("0" + currentHour).slice(-2) + ":" + ("0" + currentMinute).slice(-2);
1257
1258        if (!isTimeBooked(timeStr, bookedTimes)) {
1259          times.push(timeStr);
1260        }
1261
1262        currentMinute += timePickerIncrement;
1263        if (currentMinute >= 60) {
1264          currentMinute = 0;
1265          currentHour++;
1266        }
1267      }
1268    }
1269
1270    return times.sort();
1271  }
1272
1273  function isTimeBooked(timeStr, bookedTimes) {
1274    if (!bookedTimes) return false;
1275
1276    return bookedTimes.some(function (booking) {
1277      return (
1278        timeToMinutes(timeStr) >= timeToMinutes(booking.start) &&
1279        timeToMinutes(timeStr) <= timeToMinutes(booking.end)
1280      );
1281    });
1282  }
1283
1284  function timeToMinutes(timeStr) {
1285    var parts = timeStr.split(":");
1286    return parseInt(parts[0]) * 60 + parseInt(parts[1]);
1287  }
1288
1289  function updateSelectOptions($select, options) {
1290    // Clear existing options
1291    $select.empty();
1292
1293    // Add new options
1294    options.forEach(function (option) {
1295      $select.append(
1296        $("<option>", {
1297          value: option.value,
1298          text: option.text,
1299        })
1300      );
1301    });
1302
1303    // Refresh bootstrap-select
1304    $select.selectpicker("refresh");
1305  }
1306
1307  // $(".date-picker-listing-date_range").on(
1308  //   "apply.daterangepicker",
1309  //   function (ev, picker) {
1310  //     var startDate = picker.startDate.format("YYYY-MM-DD");
1311  //     var endDate = picker.endDate.format("YYYY-MM-DD");
1312
1313  //     // Check if selected dates include any partial bookings
1314  //     var hasPartialBookings = false;
1315  //     var current = moment(startDate);
1316  //     var end = moment(endDate);
1317
1318  //     while (current <= end) {
1319  //       var dateString = current.format("YYYY-MM-DD");
1320  //       if (partialBookedDates && partialBookedDates[dateString]) {
1321  //         hasPartialBookings = true;
1322  //         break;
1323  //       }
1324  //       current.add(1, "days");
1325  //     }
1326
1327  //     if (hasPartialBookings) {
1328  //       // Show time selection dialog
1329  //       showTimeSelectionDialog(startDate, endDate, partialBookedDates);
1330  //     }
1331
1332  //     $(this).val(
1333  //       picker.startDate.format(listeo_core.date_format) +
1334  //         " - " +
1335  //         picker.endDate.format(listeo_core.date_format)
1336  //     );
1337  //   }
1338  // );
1339
1340  // $("#date-picker").on("apply.daterangepicker", function (ev, picker) {
1341  //   const listing_id = $("#listing_id").val();
1342  //   const startDate = picker.startDate.format("YYYY-MM-DD HH:mm:ss");
1343  //   const endDate = picker.endDate.format("YYYY-MM-DD HH:mm:ss");
1344  // 	$(".date-picker-listing-date_range").removeClass("add-slot-shake-error");
1345  //   $.ajax({
1346  //     url: listeo.ajaxurl,
1347  //     type: "POST",
1348  //     data: {
1349  //       action: "check_date_range_availability",
1350  //       listing_id: listing_id,
1351  //       start_date: startDate,
1352  //       end_date: endDate,
1353  //     },
1354  //     success: function (response) {
1355  //       if (response.success) {
1356  //         if (response.data.available) {
1357  //           check_booking();
1358  //           // wait 2s and fadeOut .booking-notice-message
1359  // 		  setTimeout(function () {
1360  // 			$(".booking-notice-message").fadeOut();
1361  // 		  }, 2000);
1362  //         } else {
1363  //           if (response.data.next_available) {
1364  //             // Parse dates from response
1365  //             const newStartDate = moment(response.data.next_available.start);
1366  //             const newEndDate = moment(response.data.next_available.end);
1367  // 			$(".date-picker-listing-date_range").addClass("add-slot-shake-error");
1368  //             $(".booking-notice-message")
1369  //               .html(
1370  //                 "Selected time is not available.\n\n" +
1371  //                   "Next available time is:\n" +
1372  //                   newStartDate.format("DD-MM-YYYY HH:mm") +
1373  //                   " to " +
1374  //                   newEndDate.format("DD-MM-YYYY HH:mm")
1375  //               )
1376  //               .fadeIn();
1377
1378  //             // Update the date picker with new dates
1379  //             picker.setStartDate(newStartDate);
1380  //             picker.setEndDate(newEndDate);
1381
1382  //             // Optional: Trigger the date picker to update its display
1383  //             $(picker.element).data("daterangepicker").updateView();
1384  //             $(picker.element).data("daterangepicker").updateCalendars();
1385  //           } else {
1386  //             alert(response.data.message || "No available time slots found.");
1387  //           }
1388  //         }
1389  //       }
1390  //     },
1391  //     error: function () {
1392  //       alert("There was an error checking availability. Please try again.");
1393  //     },
1394  //   });
1395  // });
1396
1397  //   // When time is selected
1398  //   $("#date-picker").on("apply.daterangepicker", function (ev, picker) {
1399  //     const selectedDateTime = picker.startDate;
1400  //     let isValidSelection = false;
1401
1402  //     if (availableHours && availableHours.length > 0) {
1403  //       for (let slot of availableHours) {
1404  //         const slotStart = moment(slot.start);
1405  //         const slotEnd = moment(slot.end);
1406
1407  //         if (selectedDateTime.isBetween(slotStart, slotEnd, undefined, "[]")) {
1408  //           isValidSelection = true;
1409  //           break;
1410  //         }
1411  //       }
1412
1413  //       if (!isValidSelection) {
1414  //         // Find first available time
1415  //         const firstSlot = availableHours[0];
1416  //         if (firstSlot) {
1417  //           picker.setStartDate(moment(firstSlot.start));
1418  //           alert(
1419  //             "Selected time is not available. Moving to first available time."
1420  //           );
1421  //         }
1422  //         return false;
1423  //       }
1424  //     }
1425
1426  //     // If we get here, proceed with booking check
1427  //     check_booking();
1428  //   });
1429  function calculate_price() {
1430    var ajax_data = {
1431      action: "calculate_price",
1432      listing_type: $("#date-picker").data("listing_type"),
1433      listing_id: $("input#listing_id").val(),
1434      tickets: $("input#tickets").val(),
1435      coupon: $("input#coupon_code").val(),
1436      //'nonce': nonce
1437    };
1438
1439    // Add children count if present
1440    if ($(".qtyButtons.children input").length) {
1441      ajax_data.children = $(".qtyButtons.children input").val();
1442    }
1443
1444    // Add pets count if present
1445    if ($(".qtyButtons.animals input").length) {
1446      ajax_data.animals = $(".qtyButtons.animals input").val();
1447    }
1448    var services = [];
1449
1450    $.each($("input.bookable-service-checkbox:checked"), function () {
1451      var quantity = $(this)
1452        .parent()
1453        .find("input.bookable-service-quantity")
1454        .val();
1455      services.push({ service: $(this).val(), value: quantity });
1456    });
1457    ajax_data.services = services;
1458    $.ajax({
1459      type: "POST",
1460      dataType: "json",
1461      url: listeo.ajaxurl,
1462      data: ajax_data,
1463
1464      success: function (data) {
1465        $("#negative-feedback").fadeOut();
1466        $("a.book-now").removeClass("inactive");
1467        if (data.data.price) {
1468          if (listeo_core.currency_position == "before") {
1469            $(".booking-estimated-cost span").html(
1470              listeo_core.currency_symbol + " " + data.data.price
1471            );
1472          } else {
1473            $(".booking-estimated-cost span").html(
1474              data.data.price + " " + listeo_core.currency_symbol
1475            );
1476          }
1477          $(".booking-estimated-cost").data("price", data.data.price);
1478          $(".booking-estimated-cost").fadeIn();
1479        }
1480        if (data.data.price_discount) {
1481          if (listeo_core.currency_position == "before") {
1482            $(".booking-estimated-discount-cost span").html(
1483              listeo_core.currency_symbol + " " + data.data.price_discount
1484            );
1485          } else {
1486            $(".booking-estimated-discount-cost span").html(
1487              data.data.price_discount + " " + listeo_core.currency_symbol
1488            );
1489          }
1490          $(".booking-estimated-cost").addClass("estimated-with-discount");
1491          $(".booking-estimated-discount-cost").fadeIn();
1492        } else {
1493          $(".booking-estimated-cost").removeClass("estimated-with-discount");
1494          $(".booking-estimated-discount-cost").fadeOut();
1495        }
1496      },
1497    });
1498  }
1499
1500  function calculate_booking_form_price() {
1501    var ajax_data = {
1502      action: "listeo_calculate_booking_form_price",
1503      coupon: $("input#coupon_code").val(),
1504      price: $("li.total-costs").data("price"),
1505    };
1506
1507    $.ajax({
1508      type: "POST",
1509      dataType: "json",
1510      url: listeo.ajaxurl,
1511      data: ajax_data,
1512
1513      success: function (data) {
1514        if (data.price >= 0) {
1515          if (listeo_core.currency_position == "before") {
1516            $(".total-discounted_costs span").html(
1517              listeo_core.currency_symbol + " " + data.price
1518            );
1519          } else {
1520            $(".total-discounted_costs span").html(
1521              data.price + " " + listeo_core.currency_symbol
1522            );
1523          }
1524
1525          $(".total-discounted_costs").fadeIn();
1526          $(".total-costs").addClass("estimated-with-discount");
1527        } else {
1528          $(".total-discounted_costs ").fadeOut();
1529          $(".total-costs").removeClass("estimated-with-discount");
1530        }
1531      },
1532    });
1533  }
1534
1535  // function when checking booking by widget
1536  function check_booking() {
1537    inputClicked = true;
1538    if (is_open === false) return 0;
1539
1540    // if we not deal with services with slots or opening hours
1541    if (
1542      $("#date-picker").data("listing_type") == "service" &&
1543      !$("input#slot").val() &&
1544      !$(".time-picker").val()
1545    ) {
1546      $("#negative-feedback").fadeIn();
1547
1548      return;
1549    }
1550
1551    if ($("#date-picker").data("rental-timepicker")) {
1552      Cookies.set(
1553        "listeo_rental_picker_startdate",
1554        $("#date-picker")
1555          .data("daterangepicker")
1556          .startDate.format(wordpress_date_format.date + " hh:mm A")
1557      );
1558      Cookies.set(
1559        "listeo_rental_picker_enddate",
1560        $("#date-picker")
1561          .data("daterangepicker")
1562          .endDate.format(wordpress_date_format.date + " hh:mm A")
1563      );
1564      // var startDataSQL need to have hour
1565      var startDataSql = moment(
1566        $("#date-picker").data("daterangepicker").startDate,
1567        ["MM/DD/YYYY hh:mm A"]
1568      ).format("YYYY-MM-DD HH:mm:ss");
1569      var endDataSql = moment(
1570        $("#date-picker").data("daterangepicker").endDate,
1571        ["MM/DD/YYYY hh:mm A"]
1572      ).format("YYYY-MM-DD HH:mm:ss");
1573    } else {
1574      Cookies.set(
1575        "listeo_rental_startdate",
1576        $("#date-picker")
1577          .data("daterangepicker")
1578          .startDate.format(wordpress_date_format.date)
1579      );
1580      Cookies.set(
1581        "listeo_rental_enddate",
1582        $("#date-picker")
1583          .data("daterangepicker")
1584          .endDate.format(wordpress_date_format.date)
1585      );
1586      var startDataSql = moment(
1587        $("#date-picker").data("daterangepicker").startDate,
1588        ["MM/DD/YYYY"]
1589      ).format("YYYY-MM-DD");
1590      var endDataSql = moment(
1591        $("#date-picker").data("daterangepicker").endDate,
1592        ["MM/DD/YYYY"]
1593      ).format("YYYY-MM-DD");
1594    }
1595
1596    //console.log($('#date-picker').data('daterangepicker').startDate);
1597
1598    // preparing data for ajax
1599    var ajax_data = {
1600      action: "check_avaliabity",
1601      listing_type: $("#date-picker").data("listing_type"),
1602      listing_id: $("input#listing_id").val(),
1603      coupon: $("input#coupon_code").val(),
1604      date_start: startDataSql,
1605      date_end: endDataSql,
1606      //'nonce': nonce
1607    };
1608    var services = [];
1609    // $.each($("input.bookable-service-checkbox:checked"), function(){
1610    //   		services.push($(this).val());
1611    // });
1612    // $.each($("input.bookable-service-quantity"), function(){
1613    //   		services.push($(this).val());
1614    // });
1615    $.each($("input.bookable-service-checkbox:checked"), function () {
1616      var quantity = $(this)
1617        .parent()
1618        .find("input.bookable-service-quantity")
1619        .val();
1620      services.push({ service: $(this).val(), value: quantity });
1621    });
1622
1623    ajax_data.services = services;
1624
1625    if ($("input#slot").val()) ajax_data.slot = $("input#slot").val();
1626    if ($("input.adults").val()) ajax_data.adults = $("input.adults").val();
1627
1628    if ($(".time-picker").val()) ajax_data.hour = $(".time-picker").val();
1629    if ($(".time-picker-end-hour").val())
1630      ajax_data.end_hour = $(".time-picker-end-hour").val();
1631
1632    // Add children count if present
1633    if ($(".qtyButtons input.children ").length) {
1634      ajax_data.children = $(".qtyButtons input.children ").val();
1635    }
1636
1637    // Add animals count if present
1638    if ($(".qtyButtons input.animals ").length) {
1639      ajax_data.animals = $(".qtyButtons input.animals").val();
1640    }
1641
1642    // Add tickets count if present (for event/ticket listings)
1643    if ($("input#tickets").length) {
1644      ajax_data.tickets = $("input#tickets").val();
1645    }
1646
1647    // loader class
1648    $("a.book-now").addClass("loading");
1649    $("a.book-now-notloggedin").addClass("loading");
1650
1651    $.ajax({
1652      type: "POST",
1653      dataType: "json",
1654      url: listeo.ajaxurl,
1655      data: ajax_data,
1656
1657      success: function (data) {
1658      var default_error_message = $(".booking-error-message").data("default-message");
1659        $(".booking-error-message").html(default_error_message);
1660        // loader clas
1661        if (
1662          data.success == true &&
1663          (!$(".time-picker").length || is_open != false)
1664        ) {
1665          if (data.data.free_places > 0) {
1666            $("a.book-now,a.book-now-notloggedin").data(
1667              "freeplaces",
1668              data.data.free_places
1669            );
1670            // if date picker doesn't have daat attribute rental-timepicker
1671
1672            $(".booking-error-message").fadeOut();
1673          
1674            $("a.book-now").removeClass("inactive");
1675            if (data.data.price) {
1676              if (listeo_core.currency_position == "before") {
1677                $(".booking-estimated-cost span").html(
1678                  listeo_core.currency_symbol + " " + data.data.price
1679                );
1680              } else {
1681                $(".booking-estimated-cost span").html(
1682                  data.data.price + " " + listeo_core.currency_symbol
1683                );
1684              }
1685              $(".booking-estimated-cost").data("price", data.data.price);
1686              $(".booking-estimated-cost").fadeIn();
1687            } else {
1688              $(".booking-estimated-cost span").html(
1689                "0 " + listeo_core.currency_symbol
1690              );
1691              $(".booking-estimated-cost").fadeOut();
1692            }
1693            if (data.data.price_discount) {
1694              if (listeo_core.currency_position == "before") {
1695                $(".booking-estimated-discount-cost span").html(
1696                  listeo_core.currency_symbol + " " + data.data.price_discount
1697                );
1698              } else {
1699                $(".booking-estimated-discount-cost span").html(
1700                  data.data.price_discount + " " + listeo_core.currency_symbol
1701                );
1702              }
1703              $(".booking-estimated-cost").addClass("estimated-with-discount");
1704              $(".booking-estimated-discount-cost").fadeIn();
1705            } else {
1706              $(".booking-estimated-cost").removeClass(
1707                "estimated-with-discount"
1708              );
1709              $(".booking-estimated-discount-cost").fadeOut();
1710            }
1711            validate_coupon($("input#listing_id").val(), data.data.price);
1712            $(".coupon-widget-wrapper").fadeIn();
1713
1714            // Auto-submit for event/ticket listings (one-click booking)
1715            // Check if this is a ticket/event listing by presence of tickets input
1716            if ($("input#tickets").length) {
1717              // For events, auto-submit the booking after successful availability check
1718              var listing_type = $("#date-picker").data("listing_type") || $("#listing_type").val();
1719              var booking_data = {
1720                listing_type: listing_type,
1721                listing_id: $("#listing_id").val() || $("input#listing_id").val(),
1722                date_start: ajax_data.date_start,
1723                date_end: ajax_data.date_end,
1724                tickets: $("#tickets").val()
1725              };
1726
1727              if ($("#coupon_code").val()) booking_data.coupon = $("#coupon_code").val();
1728
1729              // Add services
1730              if (ajax_data.services && ajax_data.services.length > 0) {
1731                booking_data.services = ajax_data.services;
1732              }
1733
1734              $("input#booking").val(JSON.stringify(booking_data));
1735              $("#form-booking").submit();
1736            }
1737          } else {
1738            $("a.book-now,a.book-now-notloggedin").data("freeplaces", 0);
1739            // if data.data.errro exists, set the message to .booking-error-message
1740            
1741            // if (!$("#date-picker").data("rental-timepicker")) {
1742            //   $(".booking-error-message").fadeIn();
1743            // }
1744            $(".booking-error-message").fadeIn();
1745            $(".booking-estimated-cost").fadeOut();
1746
1747            $(".booking-estimated-cost span").html("");
1748          }
1749        } else {
1750          //   if (!$("#date-picker").data("rental-timepicker")) {
1751          //     $("a.book-now,a.book-now-notloggedin").data("freeplaces", 0);
1752          //     $(".booking-error-message").fadeIn();
1753          //   }
1754          
1755            if (data.error) {
1756              $(".booking-error-message").html(data.error);
1757            }
1758
1759          $(".booking-error-message").fadeIn();
1760          $(".booking-estimated-cost").fadeOut();
1761        }
1762        $("a.book-now").removeClass("loading");
1763        $("a.book-now-notloggedin").removeClass("loading");
1764      },
1765    });
1766  }
1767
1768  var is_open = true;
1769  var lastDayOfWeek;
1770
1771  // update slots and check hours setted to this day
1772  function update_booking_widget() {
1773    // function only for services
1774    if ($("#date-picker").data("listing_type") != "single_day") return;
1775
1776    $("a.book-now").addClass("loading");
1777    $("a.book-now-notloggedin").addClass("loading");
1778    // get day of week
1779
1780    var date = $("#date-picker").data("daterangepicker").endDate._d;
1781    var dayOfWeek = date.getDay() - 1;
1782
1783    if (date.getDay() == 0) {
1784      dayOfWeek = 6;
1785    }
1786
1787    var startDataSql = moment(
1788      $("#date-picker").data("daterangepicker").startDate,
1789      ["MM/DD/YYYY"]
1790    ).format("YYYY-MM-DD");
1791    var endDataSql = moment($("#date-picker").data("daterangepicker").endDate, [
1792      "MM/DD/YYYY",
1793    ]).format("YYYY-MM-DD");
1794
1795    var ajax_data = {
1796      action: "update_slots",
1797      listing_id: $("input#listing_id").val(),
1798      date_start: startDataSql,
1799      date_end: endDataSql,
1800      slot: dayOfWeek,
1801      //'nonce': nonce
1802    };
1803
1804    $.ajax({
1805      type: "POST",
1806      dataType: "json",
1807      url: listeo.ajaxurl,
1808      data: ajax_data,
1809
1810      success: function (data) {
1811        $(".time-slots-dropdown .panel-dropdown-scrollable").html(data.data);
1812
1813        // reset values of slot selector
1814        if (dayOfWeek != lastDayOfWeek) {
1815          $(".panel-dropdown-scrollable .time-slot input").prop(
1816            "checked",
1817            false
1818          );
1819
1820          $(".panel-dropdown.time-slots-dropdown input#slot").val("");
1821          $(".panel-dropdown.time-slots-dropdown a").html(
1822            $(".panel-dropdown.time-slots-dropdown a").attr("placeholder")
1823          );
1824          $(" .booking-estimated-cost span").html(" ");
1825        }
1826
1827        lastDayOfWeek = dayOfWeek;
1828
1829        if (
1830          !$(".panel-dropdown-scrollable .time-slot[day='" + dayOfWeek + "']")
1831            .length
1832        ) {
1833          $(".no-slots-information").show();
1834          $(".panel-dropdown.time-slots-dropdown a").html(
1835            $(".no-slots-information").html()
1836          );
1837        } else {
1838          // when we dont have slots for this day reset cost and show no slots
1839          $(".no-slots-information").hide();
1840          $(".panel-dropdown.time-slots-dropdown a").html(
1841            $(".panel-dropdown.time-slots-dropdown a").attr("placeholder")
1842          );
1843          $(" .booking-estimated-cost span").html(" ");
1844        }
1845        // show only slots for this day
1846        $(".panel-dropdown-scrollable .time-slot").hide();
1847
1848        $(
1849          ".panel-dropdown-scrollable .time-slot[day='" + dayOfWeek + "']"
1850        ).show();
1851        $(".time-slot").each(function () {
1852          var timeSlot = $(this);
1853          $(this)
1854            .find("input")
1855            .on("change", function () {
1856              var timeSlotVal = timeSlot.find("strong").text();
1857              var slotArray = [
1858                timeSlot.find("strong").text(),
1859                timeSlot.find("input").val(),
1860              ];
1861
1862              $(".panel-dropdown.time-slots-dropdown input#slot").val(
1863                JSON.stringify(slotArray)
1864              );
1865
1866              $(".panel-dropdown.time-slots-dropdown a").html(timeSlotVal);
1867
1868              $(".panel-dropdown").removeClass("active");
1869
1870              check_booking();
1871            });
1872        });
1873        $("a.book-now").removeClass("loading");
1874        $("a.book-now-notloggedin").removeClass("loading");
1875      },
1876    });
1877
1878    // check if opening days are active
1879    if ($(".time-picker").length) {
1880      if (availableDays) {
1881        var availableDays = JSON.parse(availableDays);
1882
1883        if (
1884          availableDays[dayOfWeek].opening == "Closed" ||
1885          availableDays[dayOfWeek].closing == "Closed"
1886        ) {
1887          $("#negative-feedback").fadeIn();
1888
1889          //$('a.book-now').css('background-color','grey');
1890
1891          is_open = false;
1892          //console.log('zamkniete tego dnia' + dayOfWeek);
1893          return;
1894        }
1895
1896        // converent hours to 24h format
1897        var opening_hour = moment(availableDays[dayOfWeek].opening, [
1898          "h:mm A",
1899        ]).format("HH:mm");
1900        var closing_hour = moment(availableDays[dayOfWeek].closing, [
1901          "h:mm A",
1902        ]).format("HH:mm");
1903
1904        // get hour in 24 format
1905        var current_hour = $(".time-picker").val();
1906
1907        // check if currer hour bar is open
1908        if (current_hour >= opening_hour && current_hour <= closing_hour) {
1909          is_open = true;
1910          $("#negative-feedback").fadeOut();
1911          $("a.book-now").attr("href", "#").css("background-color", "#f30c0c");
1912          check_booking();
1913          //console.log('otwarte' + dayOfWeek);
1914        } else {
1915          is_open = false;
1916          $("#negative-feedback").fadeIn();
1917          //$('a.book-now').attr('href','#').css('background-color','grey');
1918          $(".booking-estimated-cost span").html("");
1919          //console.log('zamkniete');
1920        }
1921      } else {
1922        check_booking();
1923      }
1924    }
1925  }
1926
1927  // if slots exist update them
1928  if ($(".time-slot").length) {
1929    
1930    update_booking_widget();
1931  }
1932
1933  // show only services for actual day from datapicker
1934  $("#date-picker").on("apply.daterangepicker", update_booking_widget);
1935  $("#date-picker").on("change", function () {
1936    
1937    check_booking();
1938    //	update_booking_widget();
1939  });
1940
1941  // when slot is selected check if there are avalible bookings
1942  $("#date-picker").on("apply.daterangepicker", check_booking);
1943  $("#date-picker").on("cancel.daterangepicker", check_booking);
1944
1945  $(document).on(
1946    "change",
1947    "input#slot,input.adults,.panel-with-children input, input.bookable-service-quantity, .form-booking-single_day input.bookable-service-checkbox,.form-booking-date_range input.bookable-service-checkbox",
1948    function (event) {
1949      check_booking();
1950    }
1951  );
1952  //$('input#slot').on( 'change', check_booking );
1953
1954  $("input#tickets,.form-booking-tickets input.bookable-service-checkbox").on(
1955    "change",
1956    function (e) {
1957      //check_booking();
1958      calculate_price();
1959    }
1960  );
1961
1962  // hours picker
1963  if ($(".time-picker").length) {
1964    var time24 = false;
1965
1966    if (listeo_core.clockformat) {
1967      time24 = true;
1968    }
1969    const calendars = $(".time-picker").flatpickr({
1970      enableTime: true,
1971      noCalendar: true,
1972      dateFormat: "H:i",
1973      time_24hr: time24,
1974      disableMobile: "true",
1975      minuteIncrement: timePickerIncrement,
1976
1977      // check if there are free days on change and calculate price
1978      onClose: function (selectedDates, dateStr, instance) {
1979        update_booking_widget();
1980        check_booking();
1981      },
1982    });
1983
1984    if ($("#_hour_end").length) {
1985      calendars[0].config.onClose = [
1986        () => {
1987          setTimeout(() => calendars[1].open(), 1);
1988        },
1989      ];
1990
1991      calendars[0].config.onChange = [
1992        (selDates) => {
1993          calendars[1].set("minDate", selDates[0]);
1994        },
1995      ];
1996      // 		 calendars[0].config.onChange = [
1997      //      (selDates) => {
1998      //        if (selDates[0]) {
1999      //          let minEndDate = new Date(selDates[0]);
2000      //          minEndDate.setHours(minEndDate.getHours() + 4);
2001      //          calendars[1].set("minDate", minEndDate);
2002      //        }
2003      //      },
2004      //    ];
2005
2006      calendars[1].config.onChange = [
2007        (selDates) => {
2008          calendars[0].set("maxDate", selDates[0]);
2009        },
2010      ];
2011    }
2012  }
2013
2014  /*----------------------------------------------------*/
2015  /*  Bookings Dashboard Script
2016/*----------------------------------------------------*/
2017  $(".booking-services").on("click", ".qtyInc", function () {
2018    var $button = $(this);
2019
2020    var oldValue = $button.parent().find("input").val();
2021    //console.log(oldValue);
2022    if (oldValue == 2) {
2023      //$button.parents('.single-service').find('label').trigger('click');
2024      $button
2025        .parents(".single-service")
2026        .find("input.bookable-service-checkbox")
2027        .prop("checked", true);
2028      updateCounter();
2029    }
2030    if ($("#form-booking").hasClass("form-booking-tickets")) {
2031      calculate_price();
2032    } else {
2033      check_booking();
2034    }
2035    //calculate_price();
2036  });
2037
2038  if ($("#booking-date-range").length) {
2039    // to update view with bookin
2040
2041    var bookingsOffset = 0;
2042
2043    // here we can set how many bookings per page
2044    var bookingsLimit = 5;
2045
2046    // function when checking booking by widget
2047    function listeo_bookings_manage(page) {
2048      if ($("#booking-date-range").data("daterangepicker")) {
2049        var startDataSql = moment(
2050          $("#booking-date-range").data("daterangepicker").startDate,
2051          ["MM/DD/YYYY"]
2052        ).format("YYYY-MM-DD");
2053        var endDataSql = moment(
2054          $("#booking-date-range").data("daterangepicker").endDate,
2055          ["MM/DD/YYYY"]
2056        ).format("YYYY-MM-DD");
2057      } else {
2058        var startDataSql = "";
2059        var endDataSql = "";
2060      }
2061      if (!page) {
2062        page = 1;
2063      }
2064      if (page == "prev") {
2065        var current = $("div.pagination-container li.current").data("paged");
2066        page = current - 1;
2067      }
2068      if (page == "next") {
2069        var current = $("div.pagination-container li.current").data("paged");
2070        page = current + 1;
2071      }
2072
2073      // preparing data for ajax
2074      var ajax_data = {
2075        action: "listeo_bookings_manage",
2076        date_start: startDataSql,
2077        date_end: endDataSql,
2078        listing_id: $("#listing_id").val(),
2079        listing_status: $("#listing_status").val(),
2080        booking_author: $("#booking_author").val(),
2081        dashboard_type: $("#dashboard_type").val(),
2082        limit: bookingsLimit,
2083        offset: bookingsOffset,
2084        page: page,
2085        //'nonce': nonce
2086      };
2087
2088      // display loader class
2089      $(".dashboard-list-box").addClass("loading");
2090
2091      $.ajax({
2092        type: "POST",
2093        dataType: "json",
2094        url: listeo.ajaxurl,
2095        data: ajax_data,
2096
2097        success: function (data) {
2098          // display loader class
2099          $(".dashboard-list-box").removeClass("loading");
2100
2101          if (data.data.html) {
2102            $("#no-bookings-information").hide();
2103            $("ul#booking-requests").html(data.data.html);
2104            $(".pagination-container").html(data.data.pagination);
2105          } else {
2106            $("ul#booking-requests").empty();
2107            $(".pagination-container").empty();
2108            $("#no-bookings-information").show();
2109          }
2110        },
2111      });
2112    }
2113
2114    $("#dashboard-booking-author-search").autocomplete({
2115      source: function (req, response) {
2116        $.getJSON(
2117          listeo_core.ajax_url +
2118            "?callback=?&action=listeo_core_incremental_listing_suggest",
2119          req,
2120          response
2121        );
2122      },
2123      select: function (event, ui) {
2124        window.location.href = ui.item.link;
2125      },
2126      minLength: 3,
2127    });
2128
2129    // hooks for get bookings into view
2130    $("#booking-date-range").on("apply.daterangepicker", function (e) {
2131      listeo_bookings_manage();
2132    });
2133    $("#listing_id").on("change", function (e) {
2134      listeo_bookings_manage();
2135    });
2136    $("#listing_status").on("change", function (e) {
2137      listeo_bookings_manage();
2138    });
2139    $("#booking_author").on("change", function (e) {
2140      listeo_bookings_manage();
2141    });
2142
2143    $("div.pagination-container").on("click", "a", function (e) {
2144      e.preventDefault();
2145
2146      var page = $(this).parent().data("paged");
2147
2148      listeo_bookings_manage(page);
2149
2150      $("body, html").animate(
2151        {
2152          scrollTop: $(".dashboard-list-box").offset().top,
2153        },
2154        600
2155      );
2156
2157      return false;
2158    });
2159
2160    $(document).on("click", ".reject, .cancel", function (e) {
2161      e.preventDefault();
2162      if (window.confirm(listeo_core.areyousure)) {
2163        var $this = $(this);
2164        $this.parents("li").addClass("loading");
2165        var status = "confirmed";
2166        if ($(this).hasClass("reject")) status = "cancelled";
2167        if ($(this).hasClass("cancel")) status = "cancelled";
2168
2169        // preparing data for ajax
2170        var ajax_data = {
2171          action: "listeo_bookings_manage",
2172          booking_id: $(this).data("booking_id"),
2173          status: status,
2174          //'nonce': nonce
2175        };
2176        $.ajax({
2177          type: "POST",
2178          dataType: "json",
2179          url: listeo.ajaxurl,
2180          data: ajax_data,
2181
2182          success: function (data) {
2183            // display loader class
2184            $this.parents("li").removeClass("loading");
2185
2186            listeo_bookings_manage();
2187          },
2188        });
2189      }
2190    });
2191
2192    $(document).on("click", ".delete", function (e) {
2193      e.preventDefault();
2194      if (window.confirm(listeo_core.areyousure)) {
2195        var $this = $(this);
2196        $this.parents("li").addClass("loading");
2197        var status = "deleted";
2198
2199        // preparing data for ajax
2200        var ajax_data = {
2201          action: "listeo_bookings_manage",
2202          booking_id: $(this).data("booking_id"),
2203          status: status,
2204          //'nonce': nonce
2205        };
2206        $.ajax({
2207          type: "POST",
2208          dataType: "json",
2209          url: listeo.ajaxurl,
2210          data: ajax_data,
2211
2212          success: function (data) {
2213            // display loader class
2214            $this.parents("li").removeClass("loading");
2215
2216            listeo_bookings_manage();
2217          },
2218        });
2219      }
2220    });
2221
2222    $(document).on("click", ".renew_booking", function (e) {
2223      e.preventDefault();
2224      if (window.confirm(listeo_core.areyousure)) {
2225        var $this = $(this);
2226        $this.parents("li").addClass("loading");
2227
2228        // preparing data for ajax
2229        var ajax_data = {
2230          action: "listeo_bookings_renew_booking",
2231          booking_id: $(this).data("booking_id"),
2232          //'nonce': nonce
2233        };
2234        $.ajax({
2235          type: "POST",
2236          dataType: "json",
2237          url: listeo.ajaxurl,
2238          data: ajax_data,
2239
2240          success: function (data) {
2241            if (data.success) {
2242            } else {
2243              alert(listeo_core.booked_dates);
2244            }
2245            // display loader class
2246            $this.parents("li").removeClass("loading");
2247
2248            listeo_bookings_manage();
2249          },
2250        });
2251      }
2252    });
2253
2254    $(document).on("click", ".approve", function (e) {
2255      e.preventDefault();
2256      var $this = $(this);
2257      $this.parents("li").addClass("loading");
2258      var status = "confirmed";
2259      if ($(this).hasClass("reject")) status = "cancelled";
2260      if ($(this).hasClass("cancel")) status = "cancelled";
2261
2262      // preparing data for ajax
2263      var ajax_data = {
2264        action: "listeo_bookings_manage",
2265        booking_id: $(this).data("booking_id"),
2266        status: status,
2267        //'nonce': nonce
2268      };
2269      $.ajax({
2270        type: "POST",
2271        dataType: "json",
2272        url: listeo.ajaxurl,
2273        data: ajax_data,
2274
2275        success: function (data) {
2276          // display loader class
2277          $this.parents("li").removeClass("loading");
2278
2279          listeo_bookings_manage();
2280        },
2281      });
2282    });
2283    $(document).on("click", ".mark-as-paid", function (e) {
2284      e.preventDefault();
2285      var $this = $(this);
2286      $this.parents("li").addClass("loading");
2287      var status = "paid";
2288
2289      // preparing data for ajax
2290      var ajax_data = {
2291        action: "listeo_bookings_manage",
2292        booking_id: $(this).data("booking_id"),
2293        status: status,
2294               //'nonce': nonce
2295      };
2296      $.ajax({
2297        type: "POST",
2298        dataType: "json",
2299        url: listeo.ajaxurl,
2300        data: ajax_data,
2301
2302        success: function (data) {
2303          // display loader class
2304          $this.parents("li").removeClass("loading");
2305
2306          listeo_bookings_manage();
2307        },
2308      });
2309    });
2310
2311    var start = moment().subtract(30, "days");
2312    var end = moment();
2313
2314    function cb(start, end) {
2315      $("#booking-date-range span,#chart-date-range span").html(
2316        start.format("MMMM D, YYYY") + " - " + end.format("MMMM D, YYYY")
2317      );
2318    }
2319
2320    var ranges = new Object();
2321    ranges[listeo_core.today] = [moment().subtract(1, "days"), moment()];
2322    ranges[listeo_core.yesterday] = [
2323      moment().subtract(1, "days"),
2324      moment().subtract(1, "days"),
2325    ];
2326    ranges[listeo_core.last_7_days] = [moment().subtract(6, "days"), moment()];
2327    ranges[listeo_core.last_30_days] = [
2328      moment().subtract(29, "days"),
2329      moment(),
2330    ];
2331    ranges[listeo_core.this_month] = [
2332      moment().startOf("month"),
2333      moment().endOf("month"),
2334    ];
2335    ranges[listeo_core.last_month] = [
2336      moment().subtract(1, "month").startOf("month"),
2337      moment().subtract(1, "month").endOf("month"),
2338    ];
2339
2340    var today = listeo_core.today;
2341
2342    $("#booking-date-range-enabler").on("click", function (e) {
2343      e.preventDefault();
2344      $(this).hide();
2345      cb(start, end);
2346      $("#booking-date-range,#chart-date-range")
2347        .show()
2348        .daterangepicker(
2349          {
2350            opens: "left",
2351            autoUpdateInput: false,
2352            alwaysShowCalendars: true,
2353            startDate: start,
2354            endDate: end,
2355            ranges: ranges,
2356            locale: {
2357              format: wordpress_date_format.date,
2358              firstDay: parseInt(wordpress_date_format.day),
2359              applyLabel: listeo_core.applyLabel,
2360              cancelLabel: listeo_core.cancelLabel,
2361              fromLabel: listeo_core.fromLabel,
2362              toLabel: listeo_core.toLabel,
2363              customRangeLabel: listeo_core.customRangeLabel,
2364              daysOfWeek: [
2365                listeo_core.day_short_su,
2366                listeo_core.day_short_mo,
2367                listeo_core.day_short_tu,
2368                listeo_core.day_short_we,
2369                listeo_core.day_short_th,
2370                listeo_core.day_short_fr,
2371                listeo_core.day_short_sa,
2372              ],
2373              monthNames: [
2374                listeo_core.january,
2375                listeo_core.february,
2376                listeo_core.march,
2377                listeo_core.april,
2378                listeo_core.may,
2379                listeo_core.june,
2380                listeo_core.july,
2381                listeo_core.august,
2382                listeo_core.september,
2383                listeo_core.october,
2384                listeo_core.november,
2385                listeo_core.december,
2386              ],
2387            },
2388          },
2389          cb
2390        )
2391        .trigger("click");
2392      cb(start, end);
2393    });
2394
2395    // Calendar animation and visual settings
2396    $("#booking-date-range").on("show.daterangepicker", function (ev, picker) {
2397      $(".daterangepicker").addClass(
2398        "calendar-visible calendar-animated bordered-style"
2399      );
2400      $(".daterangepicker").removeClass("calendar-hidden");
2401    });
2402    $("#booking-date-range").on("hide.daterangepicker", function (ev, picker) {
2403      $(".daterangepicker").removeClass("calendar-visible");
2404      $(".daterangepicker").addClass("calendar-hidden");
2405    });
2406  } // end if dashboard booking
2407
2408  // $('a.reject').on('click', function() {
2409
2410  // 	console.log(picker);
2411
2412  // });
2413
2414  /*----------------------------------------------------*/
2415  /*  Time Slots Carousel Functions
2416  /*----------------------------------------------------*/
2417  if ($('.time-slots-carousel-container').length) {
2418
2419    var currentPosition = 0;
2420    var $track = $('.slot-carousel-track');
2421    var $columns = $('.slot-carousel-day-column');
2422    var columnWidth = 0;
2423    var visibleColumns = 3;
2424    var maxPosition = 0;
2425
2426    // Set Moment.js locale from WordPress
2427    if (typeof moment !== 'undefined' && typeof listeo_core !== 'undefined' && listeo_core.wp_locale) {
2428      moment.locale(listeo_core.wp_locale);
2429    }
2430
2431    // Initialize carousel on page load
2432    initializeCarousel();
2433
2434    // Handle slot selection
2435    $('.time-slots-carousel-container').on('click', '.calendar-slot-available', function(e) {
2436      e.preventDefault();
2437
2438      // Remove previous selection
2439      $('.calendar-slot').removeClass('selected');
2440
2441      // Add selection to clicked slot
2442      $(this).addClass('selected');
2443
2444      // Get slot data
2445      var slotValue = $(this).data('slot-value');
2446      var slotTime = $(this).data('slot-time');
2447
2448      // Get the date from the parent column
2449      var selectedDate = $(this).closest('.slot-carousel-day-column').data('date');
2450
2451      // Update hidden slot input
2452      $('#slot').val(JSON.stringify([slotTime, slotValue]));
2453
2454      // Set the date picker value programmatically (required for check_booking to work)
2455      if ($('#date-picker').length && selectedDate) {
2456        var datePicker = $('#date-picker').data('daterangepicker');
2457        if (datePicker && typeof moment !== 'undefined') {
2458          var momentDate = moment(selectedDate);
2459          datePicker.setStartDate(momentDate);
2460          datePicker.setEndDate(momentDate);
2461        }
2462      }
2463
2464      // Check booking availability
2465      if (typeof check_booking === 'function') {
2466        check_booking();
2467      }
2468    });
2469
2470    // Navigation buttons - move by visible columns count
2471    $('.slot-carousel-nav-btn.prev-day').on('click', function() {
2472      if (currentPosition > 0) {
2473        currentPosition = Math.max(0, currentPosition - visibleColumns);
2474        updateCarouselPosition();
2475      }
2476    });
2477
2478    $('.slot-carousel-nav-btn.next-day').on('click', function() {
2479      if (currentPosition < maxPosition) {
2480        currentPosition = Math.min(maxPosition, currentPosition + visibleColumns);
2481        updateCarouselPosition();
2482
2483        // Check if we're within 7 days of the end, load more days if needed
2484        var totalColumns = $columns.length;
2485        var daysFromEnd = totalColumns - (currentPosition + visibleColumns);
2486        if (daysFromEnd <= 7) {
2487          appendMoreDays(14);
2488        }
2489      }
2490    });
2491
2492    // Append more days to carousel dynamically using AJAX
2493    function appendMoreDays(daysToAdd) {
2494      if (typeof moment === 'undefined') return;
2495
2496      // Get the last loaded date from data attribute
2497      var $container = $('.time-slots-carousel-container');
2498      var lastLoadedDate = $container.data('availability-loaded-until');
2499
2500      if (!lastLoadedDate) {
2501        // Fallback to last column's date if attribute not found
2502        var $lastColumn = $('.slot-carousel-day-column').last();
2503        if (!$lastColumn.length) return;
2504        lastLoadedDate = $lastColumn.data('date');
2505      }
2506
2507      var startDate = moment(lastLoadedDate).add(1, 'days');
2508      var endDate = moment(startDate).add(daysToAdd - 1, 'days');
2509
2510      // Make AJAX request for availability
2511      $.ajax({
2512        type: 'POST',
2513        url: listeo.ajaxurl,
2514        data: {
2515          action: 'get_carousel_slots_availability',
2516          listing_id: $('#listing_id').val(),
2517          date_start: startDate.format('YYYY-MM-DD'),
2518          date_end: endDate.format('YYYY-MM-DD')
2519        },
2520        success: function(response) {
2521          if (response.success && response.data) {
2522            var availabilityData = response.data;
2523
2524            // Iterate through each day in the response
2525            for (var dateStr in availabilityData) {
2526              if (!availabilityData.hasOwnProperty(dateStr)) continue;
2527
2528              var currentDate = moment(dateStr);
2529              var dayOfWeek = (currentDate.isoWeekday() % 7);
2530              var daySlots = availabilityData[dateStr];
2531              var hasSlots = Array.isArray(daySlots) && daySlots.length > 0;
2532
2533              // Get day name and date with translations
2534              var dayName = currentDate.format('ddd,');
2535
2536              // Get translated month abbreviation
2537              var monthNames = [
2538                listeo_core.month_abbrev_jan, listeo_core.month_abbrev_feb, listeo_core.month_abbrev_mar,
2539                listeo_core.month_abbrev_apr, listeo_core.month_abbrev_may, listeo_core.month_abbrev_jun,
2540                listeo_core.month_abbrev_jul, listeo_core.month_abbrev_aug, listeo_core.month_abbrev_sep,
2541                listeo_core.month_abbrev_oct, listeo_core.month_abbrev_nov, listeo_core.month_abbrev_dec
2542              ];
2543              var monthIndex = currentDate.month();
2544              var dateShort = currentDate.format('D') + ' ' + monthNames[monthIndex];
2545
2546              // Build HTML for the new column
2547              var columnHtml = '<div class="slot-carousel-day-column" data-day="' + dayOfWeek + '" data-date="' + dateStr + '">';
2548              columnHtml += '<div class="calendar-day ' + (hasSlots ? 'day-available' : 'day-placeholder') + '">';
2549              columnHtml += '<div class="calendar-day-date">';
2550              columnHtml += '<p class="day-name">' + dayName + '</p>';
2551              columnHtml += '<p class="day-date">' + dateShort + '</p>';
2552              columnHtml += '</div>';
2553              columnHtml += '<div class="calendar-day-slots">';
2554              columnHtml += '<ul class="calendar-day-slots-list">';
2555
2556              if (hasSlots) {
2557                // Add actual available slots
2558                var maxVisible = 10;
2559                var totalSlots = daySlots.length;
2560
2561                for (var j = 0; j < totalSlots; j++) {
2562                  var slot = daySlots[j];
2563                  var timeRange = slot.time;
2564                  var available = slot.available;
2565                  var slotKey = slot.slot_key;
2566                  var slotDayOfWeek = slot.day_of_week;
2567                  var isBooked = slot.is_booked || false;
2568
2569                  var timeParts = timeRange.split('-');
2570                  var startTime = timeParts[0].trim();
2571
2572                  // Hide slots after the 10th
2573                  var hiddenClass = (j >= maxVisible) ? ' hidden-slot' : '';
2574
2575                  // Determine slot class based on booked status
2576                  var slotClass = isBooked ? 'calendar-slot calendar-slot-booked' : 'calendar-slot calendar-slot-available';
2577                  var slotDisabled = isBooked ? ' disabled' : '';
2578
2579                  columnHtml += '<li class="calendar-day-slots-list-item' + hiddenClass + '">';
2580                  columnHtml += '<button type="button" class="' + slotClass + '"';
2581                  columnHtml += ' data-slot-value="' + slotDayOfWeek + '|' + slotKey + '"';
2582                  columnHtml += ' data-slot-time="' + timeRange + '"' + slotDisabled + '>';
2583                  columnHtml += startTime;
2584                  columnHtml += '</button>';
2585                  columnHtml += '</li>';
2586                }
2587
2588                // Add "Show More" button if there are more than 10 slots
2589                if (totalSlots > maxVisible) {
2590                  var moreCount = totalSlots - maxVisible;
2591                  var showMoreText = (typeof listeo_core !== 'undefined' && listeo_core.show_more_slots)
2592                    ? listeo_core.show_more_slots.replace('%d', more
2592Count)
2593                    : 'Show ' + moreCount + ' more';
2594                  columnHtml += '<li class="calendar-day-slots-list-item show-more-item">';
2595                  columnHtml += '<button type="button" class="calendar-slot-show-more">';
2596                  columnHtml += showMoreText;
2597                  columnHtml += '</button>';
2598                  columnHtml += '</li>';
2599                }
2600              } else {
2601                // Add placeholder slots
2602                for (var p = 0; p < 6; p++) {
2603                  columnHtml += '<li class="calendar-day-slots-list-item">';
2604                  columnHtml += '<button type="button" class="calendar-slot calendar-slot-placeholder" disabled tabindex="-1">-</button>';
2605                  columnHtml += '</li>';
2606                }
2607              }
2608
2609              columnHtml += '</ul>';
2610              columnHtml += '</div>';
2611              columnHtml += '</div>';
2612              columnHtml += '</div>';
2613
2614              // Append to track
2615              $('.slot-carousel-track').append(columnHtml);
2616            }
2617
2618            // Update the last loaded date
2619            $container.data('availability-loaded-until', endDate.format('YYYY-MM-DD'));
2620            $container.attr('data-availability-loaded-until', endDate.format('YYYY-MM-DD'));
2621
2622            // Recalculate dimensions and height after new columns are fully rendered
2623            setTimeout(function() {
2624              $columns = $('.slot-carousel-day-column');
2625              calculateDimensions();
2626              updateCarouselHeight();
2627            }, 100);
2628          }
2629        },
2630        error: function(xhr, status, error) {
2631          console.error('Failed to load more carousel days:', error);
2632        }
2633      });
2634    }
2635
2636    // Initialize carousel
2637    function initializeCarousel() {
2638      calculateDimensions();
2639      updateCarouselPosition();
2640
2641      // Recalculate height and show carousel
2642      setTimeout(function() {
2643        updateCarouselHeight();
2644
2645        // Add loaded class - CSS removes max-height restriction and hides loader
2646        $('.time-slots-carousel-container').addClass('slot-carousel-loaded');
2647      }, 100);
2648
2649      // Recalculate on window resize
2650      $(window).on('resize', debounce(function() {
2651        calculateDimensions();
2652        updateCarouselPosition();
2653      }, 250));
2654    }
2655
2656    // Calculate carousel dimensions
2657    function calculateDimensions() {
2658      var viewportWidth = $('.slot-carousel-viewport').width();
2659
2660      // Determine visible columns based on screen size
2661      if ($(window).width() <= 1360) {
2662        visibleColumns = 2;
2663      } else {
2664        visibleColumns = 3;
2665      }
2666
2667      // Get actual column width from rendered element (includes gap in flex layout)
2668      if ($columns.length > 0) {
2669        var firstColumn = $columns.first();
2670        var columnActualWidth = firstColumn.outerWidth(); // Gets the actual rendered width
2671        var gap = 15; // Gap between columns
2672        columnWidth = columnActualWidth + gap; // Width + gap = distance to move
2673      } else {
2674        columnWidth = viewportWidth / visibleColumns;
2675      }
2676
2677      // Calculate max position
2678      var totalColumns = $columns.length;
2679      maxPosition = Math.max(0, totalColumns - visibleColumns);
2680
2681      // Reset position if needed
2682      if (currentPosition > maxPosition) {
2683        currentPosition = maxPosition;
2684      }
2685    }
2686
2687    // Update carousel position
2688    function updateCarouselPosition() {
2689      var translateX = -(currentPosition * columnWidth);
2690      $track.css('transform', 'translateX(' + translateX + 'px)');
2691
2692      // Update navigation button states
2693      $('.slot-carousel-nav-btn.prev-day').prop('disabled', currentPosition === 0);
2694      $('.slot-carousel-nav-btn.next-day').prop('disabled', currentPosition >= maxPosition);
2695
2696      // Update carousel height based on visible columns
2697      updateCarouselHeight();
2698    }
2699
2700    // Calculate and set carousel height based on tallest visible column
2701    function updateCarouselHeight() {
2702      var maxHeight = 0;
2703
2704      // Get visible column range
2705      var visibleStart = currentPosition;
2706      var visibleEnd = currentPosition + visibleColumns;
2707
2708      // Refresh columns reference to ensure we have the latest DOM state
2709      $columns = $('.slot-carousel-day-column');
2710
2711      // Find tallest column in visible range (avoid variable name collision)
2712      var $visibleColumnElements = $columns.slice(visibleStart, visibleEnd);
2713
2714      if ($visibleColumnElements.length === 0) return;
2715
2716      $visibleColumnElements.each(function() {
2717        var $calendarDay = $(this).find('.calendar-day');
2718
2719        if (!$calendarDay.length || !$calendarDay[0]) return;
2720
2721        // Force reflow to get accurate height
2722        var height = $calendarDay[0].offsetHeight;
2723
2724        // Get computed margins
2725        var marginTop = parseInt($(this).css('margin-top')) || 0;
2726        var marginBottom = parseInt($(this).css('margin-bottom')) || 0;
2727        var columnHeight = height + marginTop + marginBottom;
2728
2729        if (columnHeight > maxHeight) {
2730          maxHeight = columnHeight;
2731        }
2732      });
2733
2734      // Account for viewport padding
2735      var $viewport = $('.slot-carousel-viewport');
2736      var viewportPaddingTop = parseInt($viewport.css('padding-top')) || 0;
2737      var viewportPaddingBottom = parseInt($viewport.css('padding-bottom')) || 0;
2738      var totalPadding = viewportPaddingTop + viewportPaddingBottom;
2739
2740      // Set viewport height (content height + viewport padding)
2741      if (maxHeight > 0) {
2742        $viewport.css('height', (maxHeight + totalPadding) + 'px');
2743      }
2744    }
2745
2746    // Debounce function for resize
2747    function debounce(func, wait) {
2748      var timeout;
2749      return function() {
2750        var context = this, args = arguments;
2751        clearTimeout(timeout);
2752        timeout = setTimeout(function() {
2753          func.apply(context, args);
2754        }, wait);
2755      };
2756    }
2757
2758    // Handle "Show More" button clicks
2759    $('.time-slots-carousel-container').on('click', '.calendar-slot-show-more', function(e) {
2760      e.preventDefault();
2761
2762      var $calendarDay = $(this).closest('.calendar-day');
2763      $calendarDay.addClass('expanded');
2764
2765      // Recalculate height after expansion
2766      setTimeout(function() {
2767        updateCarouselHeight();
2768      }, 50);
2769    });
2770
2771    // Sync with date picker if it exists
2772    $('#date-picker').on('apply.daterangepicker', function(ev, picker) {
2773      if ($('.time-slots-carousel-container').length) {
2774        var selectedDate = picker.startDate;
2775        var targetDateStr = selectedDate.format('YYYY-MM-DD');
2776
2777        // Find the matching day column
2778        var $targetColumn = $('.carousel-day-column[data-date="' + targetDateStr + '"]');
2779
2780        if ($targetColumn.length) {
2781          var targetIndex = $targetColumn.index();
2782
2783          // Calculate position to show this column
2784          currentPosition = Math.max(0, Math.min(targetIndex, maxPosition));
2785          updateCarouselPosition();
2786        }
2787      }
2788    });
2789  }
2790
2791});
2792
2793})(this.jQuery);

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.