PageSourceSearch

https://hostandkeep.com/wp-content/plugins/homerunner/assets/js/availability-calendar.js?ver=2.5.8

js hostandkeep.com collected 2026-09-24 11:31:53 UTC 35,082 bytes, 1,110 lines download raw bytes

1(function ($, settings) {
2  $.fn.homelocalAvailabilityCalendar = function (options) {
3    const app = this;
4    const $container = $(app);
5
6    const selector = {
7      main: ".homelocal-availability-calendar",
8      calendarModal: "#homelocal-availability-calendar-modal",
9      dayCellSelector: ".homelocal-availability-calendar .week .day-of-month",
10    };
11
12    let calendarSlider = null;
13    let cellHeight = null;
14    let activeMonthIndex = 0;
15    let calendarCells = {};
16    // let initialized = false;
17    let currentDates = {
18      checkin: false,
19      checkout: false,
20    };
21
22    options = $.extend({}, options);
23    options.max_num_days = parseInt(options.max_num_days, 10) || 365;
24    options.max_iterations = options.max_num_days + 62;
25    options.base_price = parseFloat(options.base_price) || 0.0;
26    settings.rate_multiplier = parseFloat(settings.rate_multiplier) || 1.0;
27    settings.start_of_week = parseInt(settings.start_of_week, 10) || 0;
28
29    const calendarUtils = new HomeLocalCalendarUtils(
30      homerunner_calendar_utils_settings,
31      {
32        min_stay: options.min_stay,
33        max_stay: options.max_stay,
34        max_num_days: options.max_num_days,
35        timezone: options.timezone,
36      },
37    );
38
39    const dayInRange = (day, minDay, maxDay) => {
40      return (
41        new Date(day) >= new Date(minDay) && new Date(day) <= new Date(maxDay)
42      );
43    };
44
45    const debounce = (func, wait) => {
46      let timeout;
47      return function (...args) {
48        const context = this;
49        clearTimeout(timeout);
50        timeout = setTimeout(() => func.apply(context, args), wait);
51      };
52    };
53
54    const isModal =
55      options.type && options.type === "modal" && options.modal_selector;
56
57    app.getVisibleDates = () => {
58      const minDate = calendarUtils.minDate;
59      const maxDate = calendarUtils.maxDate;
60
61      let startDate = minDate
62        .plus({ month: activeMonthIndex - 1 })
63        .startOf("month");
64
65      let endDate = minDate
66        .plus({ month: activeMonthIndex + 2 })
67        .endOf("month");
68
69      if (endDate > maxDate.endOf("month")) {
70        endDate = maxDate.endOf("month");
71
72        startDate = endDate.plus({ month: -3 }).startOf("month");
73      }
74
75      if (startDate < minDate.startOf("month")) {
76        startDate = minDate.startOf("month");
77      }
78
79      return {
80        start: startDate,
81        end: endDate,
82      };
83    };
84
85    app.updateCellSize = () => {
86      if ($container.find(".calendar:visible .calendar-table").length > 0) {
87        const width = $container
88          .find(".calendar:visible .calendar-table")
89          .width();
90        cellHeight = width / 7;
91        $container.find(".calendar .calendar-table").each(function (i, table) {
92          $(table)
93            .find(".day")
94            .css({
95              height: cellHeight + "px",
96              lineHeight: cellHeight + "px",
97            });
98        });
99      }
100    };
101
102    app.isBelowMax = ({ year, month }) => {
103      return (
104        year * 12 + month <
105        calendarUtils.maxDate.getFullYear() * 12 +
106          calendarUtils.maxDate.getMonth() +
107          1
108      );
109    };
110
111    app.isAboveMin = ({ year, month }) => {
112      return (
113        year * 12 + month >
114        calendarUtils.minDate.getFullYear() * 12 +
115          calendarUtils.minDate.getMonth() -
116          1
117      );
118    };
119
120    app.addCalendar = ({ year, month }, show = false) => {
121      if (
122        $container.find(
123          '.calendar[data-y="' + year + '"][data-m="' + month + '"]',
124        ).length < 1
125      ) {
126        const $el = $("<div/>", {
127          class: "calendar",
128          "data-y": year,
129          "data-m": month,
130        }).html(app.getCalendarHtml({ year, month }));
131
132        $el.appendTo($container.find(".calendars"));
133
134        setTimeout(app.updateCellSize, 1);
135
136        if (!show) {
137          $container
138            .find('.calendar[data-y="' + year + '"][data-m="' + month + '"]')
139            .hide();
140        }
141      }
142    };
143
144    app.monthLabel = (month) => {
145      return settings.months_label[month];
146    };
147
148    app.getCalendarHtml = ({ year, month }) => {
149      const zeroise = (input) => {
150        return input < 10 ? "0" + input : input;
151      };
152
153      let html =
154        '<div class="calendar-heading">' +
155        app.monthLabel(month - 1) +
156        " " +
157        year +
158        "</div>";
159
160      // Calendar content. Disable datepicker day name / number translation.
161      html += '<div class="calendar-table">';
162
163      // Week days.
164      html += '<div class="week-days">';
165      for (var i = 0; i <= 6; i++) {
166        const weekIndex = (i + settings.start_of_week) % 7;
167        html += '<div class="day-name">';
168        html += settings.weekdays_label[weekIndex];
169        html += "</div>";
170      }
171      html += "</div>";
172
173      var w = 0; // week day
174      var c = 1; // current date
175
176      let dayOfWeek = calendarUtils.getFirstWeekday(year, month);
177
178      if (settings.start_of_week) {
179        dayOfWeek = dayOfWeek - settings.start_of_week;
180        if (dayOfWeek < 0) {
181          dayOfWeek = 7 + dayOfWeek;
182        }
183      }
184
185      dayOfWeek = dayOfWeek % 7;
186
187      const daysInMonth = calendarUtils.getMonthDayCount(year, month);
188
189      // dates loop
190      for (var i = 0; i < 6 * 7; i++) {
191        if (w == 0) {
192          // first week's day
193          html += '<div class="week">';
194        }
195
196        if (i < dayOfWeek) {
197          // previous month's day compared to active month.
198          html += '<div class="day day-placeholder 2"></div>';
199        } else if (c > daysInMonth) {
200          // next month's day compared to active month.
201          html += '<div class="day day-placeholder 3"></div>';
202        } else {
203          const date = `${year}-${zeroise(month)}-${zeroise(c)}`;
204
205          let style = "";
206          if (cellHeight) {
207            style = ` height: ${cellHeight}px; line-height: ${cellHeight}px;`;
208          }
209
210          html += `<div class="day day-of-month" data-date="${date}" style="${style}">
211						<span class="date">${c}</span>
212						<span class="note price"><span>
213					</div>`;
214
215          c++;
216        }
217
218        if (w == 7 - 1) {
219          // last week's day
220          html += "</div>";
221          w = 0;
222        } else {
223          w++;
224        }
225      }
226
227      html += "</div>";
228      return html;
229    };
230
231    app.safelyProcessVisibleRange = (processFunction) => {
232      const visibleDates = app.getVisibleDates();
233
234      const firstDate = visibleDates.start.toFormat("yyyy-MM-dd");
235      const lastDate = visibleDates.end.toFormat("yyyy-MM-dd");
236
237      app.safelyProcessDateRange(firstDate, lastDate, processFunction);
238    };
239
240    app.safelyProcessDateRange = (
241      firstDateStr,
242      lastDateStr,
243      processFunction,
244    ) => {
245      let loopCounter = 0;
246      let currDate = calendarUtils.getDateZero(firstDateStr);
247      let lastDate = calendarUtils.getDateZero(lastDateStr);
248
249      while (currDate <= lastDate && loopCounter < options.max_iterations) {
250        processFunction(currDate.toFormat("yyyy-MM-dd"));
251
252        loopCounter++;
253        currDate = currDate.plus({ day: 1 });
254      }
255
256      if (loopCounter > options.max_iterations) {
257        console.warn(
258          "Calendar date range processing reached safety limit",
259          loopCounter,
260        );
261      }
262    };
263
264    app.updateCalendarsPricing = () => {
265      app.safelyProcessVisibleRange((date) => {
266        if (calendarCells[date]) {
267          // console.log(`Skipping date ${date} as it is already processed.`);
268          return;
269        }
270
271        let classnames = ["day", "day-of-month"];
272        let showPrice = true;
273        const opts = calendarUtils.dateOptions(date);
274
275        if (calendarUtils.isToday(date)) {
276          classnames.push("today");
277        }
278
279        if (calendarUtils.isUnavailable(date)) {
280          showPrice = false;
281          classnames.push("unavailable");
282        } else if (calendarUtils.isTraped(date)) {
283          if (calendarUtils._checkinBooked(date)) {
284            classnames.push("checkin-booked");
285          } else {
286            classnames.push("checkin-blocked");
287          }
288
289          if (calendarUtils._checkoutBooked(date)) {
290            classnames.push("checkout-booked");
291          } else {
292            classnames.push("checkout-blocked");
293          }
294
295          classnames.push("trapped");
296        } else {
297          if (calendarUtils._checkinBooked(date)) {
298            classnames.push("checkin-booked");
299          } else if (!calendarUtils._canCheckin(date)) {
300            classnames.push("checkin-blocked");
301          } else {
302            classnames.push("checkin-available");
303          }
304
305          if (calendarUtils._checkoutBooked(date)) {
306            classnames.push("checkout-booked");
307          } else if (!calendarUtils._canCheckout(date)) {
308            classnames.push("checkout-blocked");
309          } else {
310            classnames.push("checkout-available");
311          }
312
313          if (
314            !calendarUtils._canCheckin(date) &&
315            !calendarUtils._canCheckout(date) &&
316            calendarUtils._canStay(date)
317          ) {
318            classnames.push("pass-through");
319          }
320        }
321
322        // unique class names
323        classnames = classnames.filter(
324          (value, index, self) => self.indexOf(value) === index,
325        );
326
327        let text = "";
328
329        if (options.show_price === "1" && showPrice) {
330          if (opts && opts.price) {
331            text = $.homelocalPrice(
332              Math.round(opts.price * settings.rate_multiplier),
333              options.currency,
334            );
335          } else {
336            text = $.homelocalPrice(
337              Math.round(options.base_price),
338              options.currency,
339            );
340          }
341        }
342
343        calendarCells[date] = {
344          classnames: classnames,
345          text: text,
346        };
347
348        const $cell = $container.find(
349          selector.dayCellSelector + '[data-date="' + date + '"]',
350        );
351        $cell.attr("class", classnames.join(" "));
352        $cell.find(".price").text(text);
353      });
354    };
355
356    app.updateCheckinState = () => {
357      const checkin = currentDates.checkin;
358      const selectableRange = calendarUtils.getSelectableRange(checkin);
359
360      $container.addClass("checkin-selected").removeClass("checkout-selected");
361
362      app.safelyProcessVisibleRange((dateStr) => {
363        const $cell = $container.find(
364          selector.dayCellSelector + '[data-date="' + dateStr + '"]',
365        );
366
367        let classnames = $cell.attr("class")
368          ? $cell.attr("class").split(" ")
369          : [];
370
371        // List of classnames to remove
372        const removeClasses = [
373          "hover",
374          "selected",
375          "checkin",
376          "checkout",
377          "selectable-first",
378          "selectable-last",
379        ];
380
381        // Filter out the unwanted classnames
382        classnames = classnames.filter((cls) => !removeClasses.includes(cls));
383
384        if (dateStr === checkin) {
385          classnames.push("selected checkin");
386        } else if (
387          !selectableRange ||
388          new Date(dateStr) < new Date(selectableRange.minDate) ||
389          new Date(dateStr) > new Date(selectableRange.maxDate)
390        ) {
391          classnames.push("disabled");
392        } else {
393          if (dateStr === selectableRange.maxDate) {
394            classnames.push("selectable selectable-last");
395          } else if (dateStr === selectableRange.minDate) {
396            classnames.push("selectable selectable-first");
397          } else if (calendarUtils.canCheckinout(checkin, dateStr).status) {
398            classnames.push("selectable");
399          } else if (calendarUtils._canStay(dateStr)) {
400            classnames.push("selectable pass-through-temp");
401          }
402        }
403
404        // unique class names
405        classnames = classnames.filter(
406          (value, index, self) => self.indexOf(value) === index,
407        );
408
409        $cell.attr("class", classnames.join(" "));
410      });
411    };
412
413    app.updateCheckinCheckoutState = () => {
414      const { checkin, checkout } = currentDates;
415
416      if (!checkin || !checkout) {
417        return;
418      }
419
420      $container.addClass("checkout-selected").removeClass("checkin-selected");
421
422      const dayCellSelector = selector.main + " .week .day-of-month";
423      const $dayCells = $container.find(dayCellSelector);
424
425      $dayCells.removeClass(
426        "disabled selectable selectable-first checkout hover checkin checkout selectable-last",
427      );
428
429      app.safelyProcessDateRange(checkin, checkout, (dateStr) => {
430        if (dayInRange(dateStr, checkin, checkout)) {
431          const $cell = $container.find(
432            selector.dayCellSelector + '[data-date="' + dateStr + '"]',
433          );
434
435          $cell.addClass("selected").removeClass("disabled pass-through-temp");
436
437          if (checkin === dateStr) {
438            $cell.addClass("checkin");
439          } else if (checkout === dateStr) {
440            $cell.addClass("checkout");
441          }
442        }
443      });
444    };
445
446    let $widget = null;
447
448    app.maybeInitPicker = () => {
449      $widget = $(`.homelocal-booking-widget`);
450      if ($widget.length > 0) {
451        $container.addClass("calendar-picker");
452        app.pickerInit();
453      }
454    };
455
456    app.pickerInit = () => {
457      const dayCellSelector = selector.main + " .week .day-of-month";
458      const $dayCells = $container.find(dayCellSelector);
459      let isLoading = false;
460
461      $widget.on("updating", () => {
462        isLoading = true;
463      });
464      $widget.on("available", () => {
465        isLoading = false;
466      });
467      $widget.on("unavailable", () => {
468        isLoading = false;
469      });
470
471      if ($("#homelocal-calendar-tip").length < 1) {
472        $("body")
473          .append(`<div id="homelocal-calendar-tip" class="homelocal-tip">
474				<div class="tip-content"></div>
475				<div class="tip-arrow"></div>
476			</div>`);
477      }
478
479      let tipTimeout = null;
480
481      const showTip = ($day, tip) => {
482        $("#homelocal-calendar-tip .tip-content").html(tip);
483
484        // calculate placement based on element position in window
485        const placement =
486          $day.offset().top + $day.height() / 2 - $(window).scrollTop() <
487          $(window).height() / 3
488            ? "bottom"
489            : "top";
490
491        $("#homelocal-calendar-tip").attr("data-placement", placement);
492
493        if (placement === "top" || placement === "bottom") {
494          $("#homelocal-calendar-tip").css({
495            left:
496              $day.offset().left +
497              $day.width() / 2 -
498              $("#homelocal-calendar-tip").width() / 2,
499          });
500
501          if (placement === "top") {
502            $("#homelocal-calendar-tip").css({
503              top:
504                $day.offset().top - $("#homelocal-calendar-tip").height() - 10,
505            });
506          } else if (placement === "bottom") {
507            $("#homelocal-calendar-tip").css({
508              top: $day.offset().top + $day.height() + 10,
509            });
510          }
511        }
512
513        $("#homelocal-calendar-tip").show();
514
515        clearTimeout(tipTimeout);
516        tipTimeout = setTimeout(() => {
517          removeTip();
518        }, 5000);
519      };
520
521      const removeTip = () => {
522        $("#homelocal-calendar-tip").hide();
523        clearTimeout(tipTimeout);
524      };
525
526      const maybeHideTip = (e) => {
527        removeTip();
528      };
529
530      $(document).on("click", maybeHideTip);
531
532      const isSelectStart = () =>
533        (currentDates.checkin && currentDates.checkout) ||
534        (!currentDates.checkin && !currentDates.checkout);
535
536      // booking widget update should only happen once for a single property
537      // regardless of how many calendars are on the page.
538      window["homelocalCalendarWidgetUpdateTimer" + options.property_id] = null;
539
540      const setCheckinDay = (e, day, reset = false) => {
541        removeTip();
542
543        if (day !== currentDates.checkin) {
544          currentDates.checkin = day;
545
546          if (reset) {
547            $(document).trigger("homelocal_booking_widget_reset");
548          }
549        }
550
551        currentDates.checkout = false;
552
553        app.updateCheckinState();
554
555        $(document).on("mouseover", dayCellSelector, onDayHover);
556      };
557
558      const setCheckoutDay = (e, day) => {
559        removeTip();
560        currentDates.checkout = day;
561
562        app.updateCheckinCheckoutState();
563
564        $(document).off("mouseover", dayCellSelector, onDayHover);
565
566        if (currentDates.checkin && currentDates.checkout) {
567          $widget.find('input[name="checkin"]').val(currentDates.checkin);
568          $widget
569            .find('input[name="checkout"]')
570            .val(currentDates.checkout)
571            .trigger("change");
572        }
573      };
574
575      const dayClicked = (e) => {
576        const $day = $(e.currentTarget);
577        const dataDate = $day.attr("data-date");
578
579        if (isLoading) {
580          showTip($day, settings.selection.availability_loading);
581        } else if (calendarUtils.isUnavailable(dataDate)) {
582          showTip($day, settings.selection.unavailable);
583        } else if (isSelectStart()) {
584          const checkinResult = calendarUtils.canCheckin(dataDate);
585
586          if (
587            calendarUtils._checkinBooked(dataDate) &&
588            !calendarUtils._canCheckin(dataDate)
589          ) {
590            showTip($day, settings.selection.booked);
591          } else if (checkinResult.status) {
592            $(document).trigger(
593              "homelocal_availability_calendar_set_checkin_" +
594                options.property_id,
595              [dataDate, true],
596            );
597          } else {
598            showTip($day, checkinResult.message);
599          }
600        } else {
601          const checkoutResult = calendarUtils.canCheckinout(
602            currentDates.checkin,
603            dataDate,
604          );
605
606          if (
607            calendarUtils._checkoutBooked(dataDate) &&
608            !calendarUtils._canCheckout(dataDate)
609          ) {
610            showTip($day, settings.selection.booked);
611          } else if (checkoutResult.status) {
612            $(document).trigger(
613              "homelocal_availability_calendar_set_checkout_" +
614                options.property_id,
615              [dataDate],
616            );
617          } else {
618            showTip($day, checkoutResult.message);
619          }
620        }
621
622        return false;
623      };
624
625      const onDayHover = (e) => {
626        const $day = $(e.currentTarget);
627        const hoverDate = $day.attr("data-date");
628        const minmaxDate = calendarUtils.getSelectableRange(
629          currentDates.checkin,
630        );
631
632        if (dayInRange(hoverDate, minmaxDate.minDate, minmaxDate.maxDate)) {
633          $dayCells.removeClass("checkout");
634
635          $dayCells.each(function () {
636            const dataDate = $(this).attr("data-date");
637            if (
638              dayInRange(dataDate, currentDates.checkin, hoverDate) &&
639              dayInRange(dataDate, minmaxDate.minDate, minmaxDate.maxDate) &&
640              dataDate !== currentDates.checkin
641            ) {
642              $(this).addClass("hover");
643            } else if (
644              dayInRange(
645                dataDate,
646                currentDates.checkin,
647                calendarUtils.prevDay(minmaxDate.minDate),
648              ) &&
649              dataDate !== currentDates.checkin
650            ) {
651              $(this).addClass("hover");
652            } else {
653              $(this).removeClass("hover");
654            }
655          });
656          $container
657            .find(selector.dayCellSelector + ".hover:last")
658            .addClass("checkout");
659        }
660      };
661
662      const moveCalendar = () => {
663        const d = new Date(currentDates.checkin),
664          year = d.getFullYear(),
665          month = d.getMonth() + 1;
666
667        if (
668          !$container
669            .find('.calendar[data-y="' + year + '"][data-m="' + month + '"]')
670            .hasClass("tns-slide-active")
671        ) {
672          const index = $container
673            .find('.calendar[data-y="' + year + '"][data-m="' + month + '"]')
674            .index();
675
676          if (calendarSlider) {
677            calendarSlider.goTo(index);
678          }
679        }
680      };
681
682      const resetSelection = () => {
683        removeTip();
684        currentDates.checkin = false;
685        currentDates.checkout = false;
686        $container.removeClass("checkin-selected checkout-selected");
687        $dayCells.removeClass(
688          "disabled selected selectable selectable-first checkout hover checkin checkout selectable-last pass-through-temp",
689        );
690      };
691
692      const widgetOnChange = (e) => {
693        currentDates.checkin = $widget
694          .find('input[name="checkin"]')
695          .val()
696          .valueOf();
697        currentDates.checkout = $widget
698          .find('input[name="checkout"]')
699          .val()
700          .valueOf();
701        if (currentDates.checkin && currentDates.checkout) {
702          moveCalendar();
703          app.updateCheckinCheckoutState();
704        }
705      };
706
707      $container.on("click", dayCellSelector, dayClicked);
708
709      $widget.on("change", 'input[name="checkin"]', () => {
710        currentDates.checkin = $widget
711          .find('input[name="checkin"]')
712          .val()
713          .valueOf();
714        if (currentDates.checkin && currentDates.checkout) {
715          moveCalendar();
716          app.updateCheckinCheckoutState();
717        } else {
718          $(document).trigger(
719            "homelocal_availability_calendar_set_checkin_" +
720              options.property_id,
721            [currentDates.checkin, false],
722          );
723        }
724      });
725
726      $widget.on("change", 'input[name="checkout"]', () => {
727        currentDates.checkout = $widget
728          .find('input[name="checkout"]')
729          .val()
730          .valueOf();
731
732        if (currentDates.checkin && currentDates.checkout) {
733          moveCalendar();
734          app.updateCheckinCheckoutState();
735
736          $(document).off("mouseover", dayCellSelector, onDayHover);
737        }
738      });
739
740      if (
741        $widget.find('input[name="checkin"]').val() &&
742        $widget.find('input[name="checkout"]').val()
743      ) {
744        widgetOnChange();
745      }
746
747      // Disable dates without pricing information.
748      $dayCells.each(function () {
749        const dataDate = $(this).attr("data-date");
750        if (!calendarUtils.dateOptions(dataDate)) {
751          $(this).addClass("unavailable");
752        }
753      });
754
755      $(document).on("show", selector.calendarModal, () => {
756        app.updateCellSize();
757        removeTip();
758      });
759
760      // Third-party event listener.
761      $(document).on(
762        "homelocal_availability_calendar_reset_selection",
763        resetSelection,
764      );
765
766      // New id based event listener.
767      $(document).on(
768        "homelocal_availability_calendar_reset_selection_" +
769          options.property_id,
770        resetSelection,
771      );
772      $(document).on(
773        "homelocal_availability_calendar_set_checkin_" + options.property_id,
774        setCheckinDay,
775      );
776      $(document).on(
777        "homelocal_availability_calendar_set_checkout_" + options.property_id,
778        setCheckoutDay,
779      );
780    };
781
782    app.renderCalendars = () => {
783      const startMonth = calendarUtils.minDate.startOf("month");
784      const endMonth = calendarUtils.maxDate.startOf("month");
785
786      const monthDiffCeiled = Math.ceil(
787        endMonth.diff(startMonth, "months").months,
788      );
789
790      // Ensure at least 2 months are rendered.
791      const monthsToRender = Math.max(monthDiffCeiled + 1, 2);
792
793      for (let i = 0; i < monthsToRender; i++) {
794        const currentMonth = startMonth.plus({ months: i });
795        app.addCalendar(
796          {
797            year: currentMonth.year,
798            month: currentMonth.month,
799          },
800          true,
801        );
802      }
803    };
804
805    app.getLoadingSvg = () => {
806      return `<svg
807  role="img"
808  width="325"
809  height="396"
810  aria-labelledby="loading-aria"
811  viewBox="0 0 325 396"
812  preserveAspectRatio="none"
813>
814  <rect
815    x="0"
816    y="0"
817    width="100%"
818    height="100%"
819    clip-path="url(#clip-path)"
820    style='fill: url("#fill");'
821  ></rect>
822  <defs>
823    <clipPath id="clip-path">
824        <rect x="235" y="80" rx="2" ry="2" width="45" height="45" /> 
825        <rect x="62" y="53" rx="3" ry="3" width="13" height="12" /> 
826        <rect x="107" y="53" rx="3" ry="3" width="13" height="12" /> 
827        <rect x="154" y="53" rx="3" ry="3" width="13" height="12" /> 
828        <rect x="204" y="53" rx="3" ry="3" width="13" height="12" /> 
829        <rect x="247" y="53" rx="3" ry="3" width="13" height="12" /> 
830        <rect x="294" y="53" rx="3" ry="3" width="13" height="12" /> 
831        <rect x="282" y="80" rx="2" ry="2" width="45" height="45" /> 
832        <rect x="282" y="127" rx="2" ry="2" width="45" height="45" /> 
833        <rect x="282" y="174" rx="2" ry="2" width="45" height="45" /> 
834        <rect x="282" y="221" rx="2" ry="2" width="45" height="45" /> 
835        <rect x="282" y="268" rx="2" ry="2" width="45" height="45" /> 
836        <rect x="235" y="127" rx="2" ry="2" width="45" height="45" /> 
837        <rect x="188" y="127" rx="2" ry="2" width="45" height="45" /> 
838        <rect x="141" y="127" rx="2" ry="2" width="45" height="45" /> 
839        <rect x="94" y="127" rx="2" ry="2" width="45" height="45" /> 
840        <rect x="47" y="127" rx="2" ry="2" width="45" height="45" /> 
841        <rect x="0" y="127" rx="2" ry="2" width="45" height="45" /> 
842        <rect x="0" y="174" rx="2" ry="2" width="45" height="45" /> 
843        <rect x="0" y="221" rx="2" ry="2" width="45" height="45" /> 
844        <rect x="0" y="268" rx="2" ry="2" width="45" height="45" /> 
845        <rect x="0" y="315" rx="2" ry="2" width="45" height="45" /> 
846        <rect x="47" y="268" rx="2" ry="2" width="45" height="45" /> 
847        <rect x="94" y="268" rx="2" ry="2" width="45" height="45" /> 
848        <rect x="141" y="268" rx="2" ry="2" width="45" height="45" /> 
849        <rect x="235" y="268" rx="2" ry="2" width="45" height="45" /> 
850        <rect x="235" y="221" rx="2" ry="2" width="45" height="45" /> 
851        <rect x="188" y="221" rx="2" ry="2" width="45" height="45" /> 
852        <rect x="141" y="221" rx="2" ry="2" width="45" height="45" /> 
853        <rect x="94" y="221" rx="2" ry="2" width="45" height="45" /> 
854        <rect x="47" y="221" rx="2" ry="2" width="45" height="45" /> 
855        <rect x="47" y="174" rx="2" ry="2" width="45" height="45" /> 
856        <rect x="94" y="174" rx="2" ry="2" width="45" height="45" /> 
857        <rect x="141" y="174" rx="2" ry="2" width="45" height="45" /> 
858        <rect x="188" y="174" rx="2" ry="2" width="45" height="45" /> 
859        <rect x="235" y="174" rx="2" ry="2" width="45" height="45" /> 
860        <rect x="188" y="268" rx="2" ry="2" width="45" height="45" /> 
861        <rect x="100" y="10" rx="2" ry="2" width="125" height="22" /> 
862        <rect x="16" y="52" rx="3" ry="3" width="13" height="12" />
863    </clipPath>
864    <linearGradient id="fill">
865      <stop
866        offset="0.599964"
867        stop-color="#f3f3f3"
868        stop-opacity="1"
869      >
870        <animate
871          attributeName="offset"
872          values="-2; -2; 1"
873          keyTimes="0; 0.25; 1"
874          dur="2s"
875          repeatCount="indefinite"
876        ></animate>
877      </stop>
878      <stop
879        offset="1.59996"
880        stop-color="#ecebeb"
881        stop-opacity="1"
882      >
883        <animate
884          attributeName="offset"
885          values="-1; -1; 2"
886          keyTimes="0; 0.25; 1"
887          dur="2s"
888          repeatCount="indefinite"
889        ></animate>
890      </stop>
891      <stop
892        offset="2.59996"
893        stop-color="#f3f3f3"
894        stop-opacity="1"
895      >
896        <animate
897          attributeName="offset"
898          values="0; 0; 3"
899          keyTimes="0; 0.25; 1"
900          dur="2s"
901          repeatCount="indefinite"
902        ></animate>
903      </stop>
904    </linearGradient>
905  </defs>
906</svg>`;
907    };
908
909    app.loadPrice = (done = null) => {
910      $(document).trigger("homelocal_availability_calendar_loading", [
911        options.property_id,
912      ]);
913
914      $container.addClass("hfse-loading");
915
916      $.ajax({
917        url: `${settings.wp_api}wp/v2/properties/${options.property_id}/calendar`,
918        type: "GET",
919        data: {
920          _: new Date().getTime(),
921        },
922        beforeSend: function (xhr) {
923          if (settings.user_nonce) {
924            xhr.setRequestHeader("X-Homerunner-Nonce", settings.user_nonce);
925          }
926        },
927      })
928        .done((data) => {
929          if (typeof done === "function") {
930            done(data);
931          }
932
933          $(document).trigger("homelocal_availability_calendar_loaded", [
934            options.property_id,
935            data,
936          ]);
937        })
938        .always(() => {
939          $container.removeClass("hfse-loading");
940        });
941    };
942
943    app.init = () => {
944      $container.append(
945        `
946				${app.getDiscountContent()} 
947				<div class="local-time" style="display:none;"></div>
948				<div class="homelocal-availability-calendar-loader">
949					<div class="calendar">
950						${app.getLoadingSvg()}
951					</div>
952					<div class="calendar">
953						${app.getLoadingSvg()}
954					</div>
955				</div>
956				<div class="notranslate homelocal-availability-calendar" style="display:none;">
957					<div class="calendars"></div>
958					<button class="next-btn" type="button">${settings.next_icon_svg}</button>
959					<button class="prev-btn" type="button">${settings.prev_icon_svg}</button>
960				</div>
961				`,
962      );
963
964      app.initCalendar();
965
966      if (options.show_local_time === "1") {
967        app.initLocalTime();
968      }
969
970      if (isModal) {
971        $(document).on("beforeshow", options.modal_selector, function () {
972          // if (!initialized) {
973          //   app.initCalendar();
974          // }
975
976          app.updateCellSize();
977        });
978
979        $(`${options.modal_selector} .hk-modal-body`).on(
980          "scroll",
981          debounce(app.onCalendarModalScroll, 50),
982        );
983      } else {
984        // app.initCalendar();
985      }
986    };
987
988    app.getDiscountContent = () => {
989      if (options.show_discount_text === "1" && options.discount_text) {
990        return `<div class="discount-text-container">
991					<span class="discount-icon">
992						<svg width="30" height="30" viewBox="0 0 700 700" xmlns="http://www.w3.org/2000/svg">
993							<g>
994								<path
995									d="m557.9 215.6c-9.7969-9.8008-15.398-23.277-15.398-37.102v-25.375c0-36.227-29.398-65.625-65.625-65.625h-25.375c-13.824 0-27.301-5.6016-37.102-15.398l-18.023-17.852c-25.551-25.551-67.199-25.551-92.75 0l-18.023 18.023c-9.8008 9.625-23.277 15.227-37.102 15.227h-25.375c-36.227 0-65.625 29.398-65.625 65.625v25.375c0 13.824-5.6016 27.301-15.398 37.102l-17.852 18.023c-25.551 25.551-25.551 67.199 0 92.75l18.023 18.023c9.625 9.8008 15.227 23.277 15.227 37.102v25.375c0 36.227 29.398 65.625 65.625 65.625h25.375c13.824 0 27.301 5.6016 37.102 15.398l18.023 18.023c12.773 12.777 29.574 19.078 46.375 19.078s33.602-6.4766 46.375-19.25l18.023-18.023c9.8008-9.625 23.277-15.227 37.102-15.227h25.375c36.227 0 65.625-29.398 65.625-65.625v-25.375c0-13.824 5.6016-27.301 15.398-37.102l18.023-18.023c25.551-25.551 25.551-67.199 0-92.75zm-6.8242 86.098-18.023 18.023c-16.449 16.453-25.551 38.328-25.551 61.777v25.375c0 16.977-13.824 30.625-30.625 30.625h-25.375c-23.449 0-45.324 9.1016-61.949 25.551l-18.023 18.023c-11.898 11.898-31.324 11.898-43.398 0l-18.023-18.023c-16.281-16.449-38.156-25.551-61.605-25.551h-25.375c-16.801 0-30.625-13.824-30.625-30.625v-25.375c0-23.273-9.1016-45.324-25.551-61.949l-18.023-18.023c-11.898-11.898-11.898-31.324 0-43.398l18.023-18.023c16.449-16.281 25.551-38.156 25.551-61.605v-25.375c0-16.801 13.824-30.625 30.625-30.625h25.375c23.449 0 45.324-9.1016 61.949-25.551l18.023-18.023c11.898-11.898 31.324-11.898 43.398 0l18.023 18.023c16.281 16.449 38.156 25.551 61.605 25.551h25.375c16.801 0 30.625 13.824 30.625 30.625v25.375c0 23.273 9.1016 45.324 25.551 61.949l18.023 18.023c11.902 11.727 11.902 31.328 0 43.227z" />
996								<path
997									d="m425.07 180.07-175 175c-6.8242 6.8242-6.8242 17.852 0 24.676 3.5 3.5 7.875 5.25 12.426 5.25s8.9258-1.75 12.426-5.0742l175-175c6.8242-6.8242 6.8242-17.852 0-24.676s-18.027-7-24.852-0.17578z" />
998								<path d="m455 350c0 19.328-15.672 35-35 35s-35-15.672-35-35 15.672-35 35-35 35 15.672 35 35" />
999								<path d="m315 210c0 19.328-15.672 35-35 35s-35-15.672-35-35 15.672-35 35-35 35 15.672 35 35" />
1000							</g>
1001						</svg>
1002					</span>
1003					<span class="discount-text">
1004						${options.discount_text}
1005					</span>
1006				</div>`;
1007      }
1008
1009      return "";
1010    };
1011
1012    app.initLocalTime = () => {
1013      app.updateLocalTime();
1014
1015      $container.find(".local-time").show();
1016
1017      setInterval(app.updateLocalTime, 60000);
1018    };
1019
1020    app.updateLocalTime = () => {
1021      const localDate = calendarUtils.getDateNow();
1022      $container.find(".local-time").html(
1023        `<span class="local-time-label">${settings.local_time_label}</span>
1024					<span class="local-time-datetime">
1024${localDate.toFormat(
1025            "dd LLL, hh:mm a",
1026          )}</span>`,
1027      );
1028    };
1029
1030    app.initCalendar = () => {
1031      initialized = true;
1032
1033      app.renderCalendars();
1034      app.updateCellSize();
1035      app.calendarSliderInit();
1036
1037      $(window).on("resize", app.updateCellSize);
1038
1039      app.loadPrice((data) => {
1040        calendarUtils.setPriceData(data);
1041        app.updateCalendarsPricing();
1042        app.updateCheckinCheckoutState();
1043
1044        $container.find(".homelocal-availability-calendar-loader").hide();
1045        $container.find(".homelocal-availability-calendar").show();
1046
1047        app.updateCellSize();
1048        app.maybeInitPicker();
1049      });
1050    };
1051
1052    app.onCalendarModalScroll = () => {
1053      const monthHeight = cellHeight * 7; // 7 rows per month
1054
1055      const scrollTop = $(
1056        `${options.modal_selector} .hk-modal-body`,
1057      ).scrollTop();
1058
1059      activeMonthIndex = Math.floor(scrollTop / monthHeight);
1060      app.updateCalendarsPricing();
1061      app.updateCheckinCheckoutState();
1062    };
1063
1064    app.calendarSliderInit = () => {
1065      if (options.type && options.type === "modal") {
1066        const viewportWidth = $(window).width();
1067        if (viewportWidth < 768) {
1068          if (calendarSlider) {
1069            calendarSlider.destroy();
1070            calendarSlider = null;
1071          }
1072          return;
1073        }
1074      }
1075
1076      if (calendarSlider) {
1077        return;
1078      }
1079
1080      calendarSlider = tns({
1081        container: `#${options.app_id} .calendars`,
1082        items: 1,
1083        gutter: 0,
1084        responsive: {
1085          768: {
1086            items: 2,
1087            gutter: 30,
1088          },
1089        },
1090        slideBy: 1,
1091        speed: 600,
1092        autoplay: false,
1093        mouseDrag: true,
1094        // preventActionWhenRunning: true,
1095        nav: false,
1096        loop: false,
1097        nextButton: `#${options.app_id} .next-btn`,
1098        prevButton: `#${options.app_id} .prev-btn`,
1099      });
1100
1101      calendarSlider.events.on("transitionEnd", () => {
1102        activeMonthIndex = calendarSlider.getInfo().index;
1103        app.updateCalendarsPricing();
1104        app.updateCheckinCheckoutState();
1105      });
1106    };
1107
1108    app.init();
1109  };
1110})(jQuery, homerunner_availability_calendar_settings);

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.