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