PageSourceSearch

https://tourismmcgregor.co.za/wp-content/plugins/listeo-core/assets/js/bookings.js?ver=2.0.20

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