PageSourceSearch

https://ostrovskiyhotel.ru/assets/2f2d527c/js/widget.js?v=1791278912

js ostrovskiyhotel.ru collected 2026-10-06 09:28:40 UTC 31,750 bytes, 946 lines download raw bytes

1class JCalendar {
2
3    firstDate = null;
4    secondDate = null;
5
6    firstDateF = null;
7    secondDateF = null;
8
9    firstDateId = "firstDateId";
10    secondDateId = "secondDateId";
11    dayCellClass = ".calendar-day";
12    finished = false;
13    debug = true;
14    isCanDraw = true;
15
16    constructor(firstDate = null, secondDate = null, canDraw = true) {
17        this.firstDate = firstDate;
18        this.secondDate = secondDate;
19        this.isCanDraw = canDraw;
20        this.firstDateF = null;
21        this.secondDateF = null;
22        this.firstDateId = "firstDateId";
23        this.secondDateId = "secondDateId";
24        this.dayCellClass = ".calendar-day";
25        this.finished = false;
26        this.debug = true;
27    };
28
29    resetIfFinished() {
30        return true;
31    }
32
33    reset() {
34        this.firstDate = null;
35        this.secondDate = null;
36        this.finished = false;
37        this.onChangeDate();
38    }
39
40    swapDates() {
41        let tmp = this.firstDate;
42        let tmpF = this.firstDateF;
43
44        if (this.isSecondDateSetted() && (this.secondDate > this.firstDate)) {
45            return;
46        }
47
48        this.firstDate = this.secondDate;
49        this.firstDateF = this.secondDateF;
50        this.secondDate = tmp;
51        this.secondDateF = tmpF;
52        this.onChangeDate();
53    }
54
55    setFirstDate(timestamp, formattedDate) {
56        this.firstDate = timestamp;
57        this.firstDateF = formattedDate;
58        this.onChangeDate(formattedDate);
59    }
60
61    setSecondDate(timestamp, formattedDate) {
62        this.secondDate = timestamp;
63        this.secondDateF = formattedDate;
64        this.onChangeDate(formattedDate);
65    }
66
67    showDebug(message) {
68        if (this.debug) {
69            console.log(message);
70        }
71    }
72
73    isFirstDateSetted() {
74        return !(isNaN(this.firstDate) || (this.firstDate == null));
75    }
76
77    isSecondDateSetted() {
78        return !(isNaN(this.secondDate) || (this.secondDate == null));
79    }
80
81    isDatesSetted() {
82        return (this.isFirstDateSetted() && this.isSecondDateSetted());
83    }
84
85    isFinished() {
86        return this.finished;
87    }
88
89    canDraw() {
90        return this.isCanDraw;
91    }
92
93    correctSwapDates() {
94        if (!this.isDatesSetted()) {
95            return;
96        }
97
98        if (this.secondDate < this.firstDate) {
99            let tmp = this.firstDate;
100            this.firstDate = this.secondDate;
101            this.secondDate = tmp;
102        }
103    }
104
105    formatDateWithZeros(timestamp) {
106
107        if(timestamp === null){
108            return " ";
109        }
110
111        const date = new Date(timestamp);
112        if (isNaN(date)) {
113            return " ";
114        }
115        const day = ("0" + date.getDate()).slice(-2);
116        const month = ("0" + (date.getMonth() + 1)).slice(-2);
117        const year = date.getFullYear();
118        return `${day}.${month}.${year}`;
119    }
120
121    onChangeDate() {
122        const $firstInput = $('#' + this.firstDateId);
123        const $secondInput = $('#' + this.secondDateId);
124
125        if ($firstInput.length) {
126            $firstInput.val(this.firstDate);
127            $firstInput.parent('.calendar-fake-input').css('border', '');
128        }
129
130        if ($secondInput.length) {
131            $secondInput.val(this.secondDate);
132            $secondInput.parent('.calendar-fake-input').css('border', '');
133        }
134
135        const firstFormatted = this.formatDateWithZeros(this.firstDate);
136        $('#firstDateInputText').html(firstFormatted);
137        if (firstFormatted !== " ") {
138            this.showDebug("Date: " + firstFormatted);
139        }
140
141        const secondFormatted = this.formatDateWithZeros(this.secondDate);
142        $('#secondDateInputText').html(secondFormatted);
143        if (secondFormatted !== " ") {
144            this.showDebug("Date: " + secondFormatted);
145        }
146
147        if (this.canDraw()) this.draw();
148    }
149
150    isDayBeforeFirst(timestamp) {
151        if (this.firstDate && this.firstDate > timestamp) {
152            return true;
153        }
154        return false;
155    }
156
157    isDayAfterFirst(timestamp) {
158        if (this.isFirstDateSetted() && this.firstDate < timestamp) {
159            return true;
160        }
161        return false;
162    }
163
164    /**
165     *  метод проверяет даты на забюлокированные дни, в дальнейшем сюда можно дописать правила бронирования... хотя и выглядит сложным такая доработка
166     */
167    checkDates(){
168        var params = [this];
169        $(this.dayCellClass).each(function () {
170
171            let disabled = $(this).hasClass("disabled");
172            let disabledcheckin = $(this).hasClass("disabled-checkin");
173
174            var instance = params[0];
175            let date = $(this).attr("data-date");
176            let timestamp = Date.parse(date);
177            if (isNaN(timestamp)) {
178                return;
179            }
180
181            if (instance.isSelected(timestamp)) {
182                if (instance.isFirstDay(timestamp)) {
183                    if (disabled || disabledcheckin){
184                        instance.reset();
185                        console.log("reset 1");
186                    }
187                }
188
189                if (instance.isLastDay(timestamp)) {
190                    if (disabled){
191                        instance.reset();
192                        console.log("reset 2")
193                    }
194                }
195            } else if (instance.isDayInRange(timestamp)) {
196                if(disabled || disabledcheckin){
197                    instance.reset();
198                    console.log("reset 3")
199                }
200            } else {
201
202            }
203        });
204    }
205
206    addDay(timestamp, click = false, formattedDate = "", disabled = false, disabledCheckin = false) {
207
208        if (click) {
209            if (this.isFinished() && this.resetIfFinished()) {
210                this.reset();
211            }
212
213            if(disabled){
214                return;
215            }
216            if (this.isFirstDateSetted() && (this.isDayBeforeFirst(timestamp))) {
217                this.setFirstDate(timestamp, formattedDate);
218            } else if (this.isFirstDateSetted() && (this.isDayAfterFirst(timestamp))) {
219                this.setSecondDate(timestamp, formattedDate);
220            } else {
221                this.setFirstDate(timestamp, formattedDate);
222            }
223
224            this.checkDates();
225            if (this.isDatesSetted()) {
226                this.finished = true;
227            }
228            return;
229        } else {
230            if (this.isFinished()) {
231                return;
232            }
233
234            if(disabled){
235                return;
236            }
237
238            if ((this.isFirstDateSetted()) && (jCalendar.isDayBeforeFirst(timestamp))) {
239                this.swapDates();
240                this.setFirstDate(timestamp, formattedDate);
241            } else {
242                if (this.isFirstDateSetted() && (jCalendar.isDayAfterFirst(timestamp))) {
243                    this.setSecondDate(timestamp, formattedDate);
244                }
245            }
246
247            this.checkDates();
248            return;
249        }
250    }
251
252    isDayInRange(timestamp) {
253        if (!this.isDatesSetted()) {
254            return false;
255        }
256        let firstDate = Math.min(this.firstDate, this.secondDate);
257        let secondDate = Math.max(this.firstDate, this.secondDate);
258
259        return (timestamp >= firstDate && timestamp <= secondDate);
260    }
261
262    isSelected(timestamp) {
263        return (timestamp == this.firstDate || timestamp == this.secondDate);
264    }
265
266    isFirstDay(timestamp) {
267        return timestamp == this.firstDate;
268    }
269
270    isLastDay(timestamp) {
271        return timestamp == this.secondDate;
272    }
273
274    draw() {
275        if (this.isFirstDateSetted()) {
276            $('#calendar-bottom-caption-default').hide();
277            $('#calendar-bottom-caption-first').html(translations.check_in + " " + this.firstDateF);
278
279            if (this.isSecondDateSetted()) {
280                $('#calendar-bottom-caption-second').html("- " + translations.check_out + " " + this.secondDateF);
281                $('#calendar-bottom-caption-night').html(this.getNightsBetweenTimestamps());
282            } else {
283                $('#calendar-bottom-caption-second').html("");
284                $('#calendar-bottom-caption-night').html("");
285            }
286
287            var params = [this];
288            $(this.dayCellClass).each(function () {
289                var instance = params[0];
290                let date = $(this).attr("data-date");
291                let timestamp = Date.parse(date);
292                if (isNaN(timestamp)) {
293                    return;
294                }
295                let cssClass = "";
296                if (instance.isSelected(timestamp)) {
297                    cssClass = "selected";
298
299                    if (instance.isFirstDay(timestamp)) {
300                        cssClass = "selected first-day";
301                    }
302
303                    if (instance.isLastDay(timestamp)) {
304                        cssClass = "selected last-day";
305                    }
306                } else if (instance.isDayInRange(timestamp)) {
307                    cssClass = "hovered";
308                } else {
309                    cssClass = "";
310                }
311
312                $(this).removeClass("selected last-day first-day hovered");
313
314                let tooltip = $(this).children('.tooltip_place');
315                if (tooltip.length) {
316                    $(tooltip).removeClass("tooltip");
317                    $(tooltip).html("");
318                }
319
320                if (instance.isDayInRange(timestamp) || instance.isSelected(timestamp)) {
321                    $(this).addClass(cssClass);
322                }
323
324                if (instance.isDayInRange(timestamp) && instance.isSelected(timestamp)) {
325                    if (tooltip.length) {
326                        $(tooltip).addClass("tooltip");
327                        let txt = instance.getNightsBetweenTimestamps();
328                        if ($(this).hasClass("disabled-checkin")){
329                            txt += " (" + translations.checkout_only + ")";
330                        }
331                        $(tooltip).html(txt.toString());
332                    }
333                }else{
334                    $(tooltip).html("");
335                    $(tooltip).removeClass("tooltip");
336                }
337            }, params);
338        } else {
339            $('#calendar-bottom-caption-first').html("");
340            $('#calendar-bottom-caption-second').html("");
341            $('#calendar-bottom-caption-night').html("");
342            $('#calendar-bottom-caption-default').show();
343            $(this.dayCellClass).each(function () {
344                $(this).removeClass("selected hovered");
345                let tooltip = $(this).children('.tooltip_place');
346                if (tooltip.length) {
347                    $(tooltip).removeClass("tooltip");
348                    $(tooltip).html("");
349                }
350            });
351        }
352    }
353
354    getNightsWord(count) {
355        count = Math.abs(Math.floor(count));
356        const lastDigit = count % 10;
357        const lastTwoDigits = count % 100;
358
359        if (lastTwoDigits >= 11 && lastTwoDigits <= 14) {
360            return `(${count} ${translations.nights_5_plus})`;
361        }
362
363        if (lastDigit === 1) {
364            return `${count} ${translations.night}`;
365        } else if (lastDigit >= 2 && lastDigit <= 4) {
366            return `${count} ${translations.nights_2_4}`;
367        } else {
368            return `${count} ${translations.nights_5_plus}`;
369        }
370    }
371
372    getNightsBetweenTimestamps() {
373        const startDate = new Date(this.firstDate);
374        const endDate = new Date(this.secondDate);
375        if (isNaN(startDate) || isNaN(endDate)) {
376            return "";
377        }
378
379        const diffMs = endDate - startDate;
380        const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
381
382        if (diffDays < 0) {
383            return "";
384        }
385
386        return this.getNightsWord(diffDays);
387    }
388}
389
390function parseDay(day) {
391    let date = $(day).attr('data-date');
392    let res = Date.parse(date);
393    return res;
394}
395
396function findRoom() {
397    $('#loading-overlay').addClass('active').fadeIn(300);
398
399    let startDate = $('#firstDateId').val();
400    let lastDate = $('#secondDateId').val();
401    let selectedRoomTypeId = parseInt($('.room-type-input[name="selectedRoomTypeId"]').val()) || null;
402    let adults = parseInt($('.guests-select_form-input[name="adults"]').val()) || 0;
403    let children = parseInt($('.guests-select_form-input[name="children"]').val()) || 0;
404    let childUnder6 = parseInt($('.guests-select_form-input[name="childUnder6"]').val()) || 0;
405
406    $('#firstDateId').parent('.calendar-fake-input').css('border', '');
407    $('#secondDateId').parent('.calendar-fake-input').css('border', '');
408
409    let isValid = true;
410    if (!startDate) {
411        $('#firstDateId').parent('.calendar-fake-input').css('border', '1px solid #dc3545');
412        isValid = false;
413    }
414    if (!lastDate) {
415        $('#secondDateId').parent('.calendar-fake-input').css('border', '1px solid #dc3545');
416        isValid = false;
417    }
418
419    if (!isValid) {
420        $('#error-message').text(translations.please_fill_dates).show();
421        $('#loading-overlay').removeClass('active').fadeOut(300);
422        return;
423    }
424
425    let formData = [
426        { name: 'startDate', value: startDate },
427        { name: 'lastDate', value: lastDate },
428        { name: 'selectedRoomTypeId', value: selectedRoomTypeId },
429        { name: 'adults', value: adults },
430        { name: 'children', value: children },
431        { name: 'childUnder6', value: childUnder6}
432    ];
433
434    let queryString = $.param(formData);
435    let url_ = '/booking/booking/?' + queryString;
436
437    setTimeout(function() {
438        window.location.href = url_;
439    }, 50);
440}
441
442$(document).ready(function () {
443    const startDate = $('#firstDateId').val();
444    const lastDate = $('#secondDateId').val();
445    const adults = $('.guests-select__stepper-input[name="adults"]').val();
446    const children = $('.guests-select__stepper-input[name="children"]').val();
447
448    $('#childUnder6-checkbox').on('change', function() {
449        const value = $(this).is(':checked') ? 1 : 0;
450        $('.guests-select_form-input[name="childUnder6"]').val(value);
451        console.log("childUnder6 =" + $('.guests-select_form-input[name="childUnder6"]').val());
452    });
453
454    console.log("[JCalendar Init] startDate from input: " + startDate);
455    console.log("[JCalendar Init] lastDate from input: " + lastDate);
456    console.log("[Guests Init] adults from input: " + adults);
457    console.log("[Guests Init] children from input: " + children);
458
459    let i = 0;
460    jCalendar = new JCalendar();
461
462    if (startDate && !isNaN(startDate) && parseInt(startDate, 10) !== 0) {
463        const timestamp = parseInt(startDate, 10);
464        const formattedDate = jCalendar.formatDateWithZeros(timestamp);
465        if (formattedDate !== " ") {
466            jCalendar.setFirstDate(timestamp, formattedDate);
467        }
468    }
469
470    if (lastDate && !isNaN(lastDate) && parseInt(lastDate, 10) !== 0) {
471        const timestamp = parseInt(lastDate, 10);
472        const formattedDate = jCalendar.formatDateWithZeros(timestamp);
473        if (formattedDate !== " ") {
474            jCalendar.setSecondDate(timestamp, formattedDate);
475        }
476    }
477
478    if (adults && !isNaN(adults)) {
479        $('.guests-select__stepper-input[name="adults"]').val(parseInt(adults, 10));
480    }
481    if (children && !isNaN(children)) {
482        $('.guests-select__stepper-input[name="children"]').val(parseInt(children, 10));
483    }
484    updateGuestsString();
485
486    //клик для выбора дня
487    $('.j-calendar-day').click(function () {
488        timestamp = parseDay($(this));
489
490        let disabled = $(this).hasClass("disabled");
491
492        if (isNaN(timestamp)) {
493            return;
494        }
495        let formattedDate = $(this).attr("data-formatted-date");
496        jCalendar.addDay(timestamp, true, formattedDate, disabled);
497    });
498
499    //ховер для выбора дня
500    $('.j-calendar-day').hover(function () {
501        i++;
502        timestamp = parseDay($(this));
503        let disabled = $(this).hasClass("disabled");
504        let disabledCheckin = $(this).hasClass("disabled-checkin");
505        if (isNaN(timestamp)) {
506            return;
507        }
508
509        let formattedDate = $(this).attr("data-formatted-date");
510        jCalendar.addDay(timestamp, false, formattedDate, disabled, disabledCheckin);
511    });
512
513    $('#calendar-widget').modal({
514        backdrop: false,
515        keyboard: true,
516        show: false
517    });
518
519    function setGuestWidgetTopLeft() {
520        var windowWidth = $(window).width();
521        var windowHeight = $(window).height();
522        var $guestWidget = $('.guests-select-widget-dialog');
523
524        var referenceTop = $('#guest-input-container').offset().top;
525        var referenceHeight = $('#guest-input-container').outerHeight();
526        var widgetHeight = $guestWidget.outerHeight();
527        var newTop = referenceTop + referenceHeight;
528
529        var totalHeight = referenceTop + referenceHeight + widgetHeight;
530
531        console.log("windowHeight = " + windowHeight);
532        console.log("windowWidth = " + windowWidth);
533        console.log("referenceTop = " + referenceTop);
534        console.log("referenceHeight = " + referenceHeight);
535        console.log("widgetHeight = " + widgetHeight);
536        console.log("totalHeight = " + totalHeight);
537        console.log("newTop = " + newTop);
538
539        if (windowWidth < 880) {
540            var styles = {
541                'position': 'fixed',
542                'left': '50%',
543                'transform': 'translateX(-50%)',
544                'margin': '0'
545            };
546
547            if (windowHeight < totalHeight) {
548                var centeredTop = (windowHeight - widgetHeight) / 2;
549                centeredTop = Math.max(10, centeredTop);
550                console.log("Centering widget (small width), centeredTop = " + centeredTop);
551                styles.top = centeredTop + 'px';
552            } else {
553                styles.top = newTop + 'px';
554            }
555
556            $guestWidget.css(styles);
557        } else {
558            var referenceLeft = $('#guest-input-container').parent().offset().left;
559            var dialogWidth = $guestWidget.width();
560            var newLeft = referenceLeft;
561
562            if ((newLeft + dialogWidth) > windowWidth) {
563                newLeft = windowWidth - dialogWidth;
564            }
565
566            if (windowHeight < totalHeight) {
567                var centeredTop = (windowHeight - widgetHeight) / 2;
568                centeredTop = Math.max(10, centeredTop);
569                console.log("Centering widget (large width), centeredTop = " + centeredTop);
570                $guestWidget.css({
571                    'position': 'fixed',
572                    'top': centeredTop + 'px',
573                    'left': newLeft + 'px',
574                    'margin': '0'
575                });
576            } else {
577                newTop = referenceTop + referenceHeight - 15;
578                $guestWidget.css({
579                    'position': 'fixed',
580                    'top': newTop + 'px',
581                    'left': newLeft + 'px',
582                    'margin': '0'
583                });
584            }
585        }
586    }
587
588    function setCalendarWidgetTop() {
589        var windowWidth = $(window).width();
590        var windowHeight = $(window).height();
591        var $calendarWidget = $('#calendar-widget');
592
593        if (windowWidth < 880) {
594            $calendarWidget.css({
595                'top': '10px',
596                'position': 'fixed',
597                'left': '0',
598                'transform': 'none'
599            });
600        } else {
601            $calendarWidget.css({
602                'position': 'fixed',
603                'left': '50%',
604                'transform': 'translate(-50%)'
605            });
606
607            var referenceTop = $('#calendar-widget-reference-element').offset().top;
608            var referenceHeight = $('#calendar-widget-reference-element').outerHeight();
609            var widgetHeight = $calendarWidget.outerHeight();
610            var newTop = referenceTop + referenceHeight + 10;
611
612            var totalHeight = referenceTop + referenceHeight + widgetHeight + 10;
613
614            console.log("windowHeight = " + windowHeight);
615            console.log("referenceTop = " + referenceTop);
616            console.log("referenceHeight = " + referenceHeight);
617            console.log("widgetHeight = " + widgetHeight);
618            console.log("totalHeight = " + totalHeight);
619            console.log("newTop = " + newTop);
620
621            if (windowHeight < totalHeight) {
622                var centeredTop = (windowHeight - widgetHeight) / 2;
623                centeredTop = Math.max(10, centeredTop);
624                console.log("Centering widget, centeredTop = " + centeredTop);
625                $calendarWidget.css('top', centeredTop + 'px');
626            } else {
627                $calendarWidget.css('top', newTop + 'px');
628            }
629        }
630    }
631
632    $('#calendar-widget').on('shown.bs.modal', function () {
633        const backdrop = $('<div class="custom-backdrop calendar-widget-backdrop"></div>');
634        $('body').append(backdrop);
635        backdrop.fadeIn();
636        setCalendarWidgetTop();
637    });
638
639    $('#calendar-widget').on('hidden.bs.modal', function () {
640        $('.calendar-widget-backdrop').fadeOut(300, function () {
641            $(this).remove();
642        });
643    });
644
645    $('#guests-select-widget').on('shown.bs.modal', function () {
646        const backdrop = $('<div class="custom-backdrop guests-select-widget-backdrop"></div>');
647        $('body').append(backdrop);
648        backdrop.fadeIn();
649        setGuestWidgetTopLeft();
650    });
651
652    $('#guests-select-widget').on('hidden.bs.modal', function () {
653        $('.guests-select-widget-backdrop').fadeOut(300, function () {
654            $(this).remove();
655        });
656    });
657
658    $(window).resize(function() {
659        if ($('#calendar-widget').hasClass('show')) {
660            setCalendarWidgetTop();
661        }
662
663        if ($('#guests-select-widget').hasClass('show')) {
664            setGuestWidgetTopLeft();
665        }
666    });
667
668    $('#guests-select-widget').modal({
669        backdrop: false,
670        keyboard: true,
671        show: false
672    });
673
674    $('#find-room').on('click', function() {
675        findRoom();
676    });
677
678    $('.show-modal-calendar-btn').on('click', function() {
679        $('#calendar-widget').modal('show');
680    });
681
682    $('.show-modal-guest-btn').on('click', function() {
683        $('#guests-select-widget').modal('show');
684    });
685
686    $(document).on('click', '.custom-backdrop', function() {
687        $('#calendar-widget').modal('hide');
688        $('#guests-select-widget').modal('hide');
689    });
690
691    const MAX_ADULTS = 2;
692    const MIN_ADULTS = 1;
693    const MAX_TOTAL_GUESTS = 5;
694
695    function getTotalGuests() {
696        const adults = parseInt($('.guests-select__stepper-input[name="adults"]').val()) || 0;
697        const children = parseInt($('.guests-select__stepper-input[name="children"]').val()) || 0;
698        return adults + children;
699    }
700
701    function getGuestsString(adults, children) {
702        const adultWord = (count) => {
703            count = Math.abs(Math.floor(count));
704            const lastDigit = count % 10;
705            const lastTwoDigits = count % 100;
706            if (lastTwoDigits >= 11 && lastTwoDigits <= 14) return translations.adults_5_plus;
707            if (lastDigit === 1) return translations.adult;
708            if (lastDigit >= 2 && lastDigit <= 4) return translations.adults_2_4;
709            return translations.adults_5_plus;
710        };
711
712        const childWord = (count) => {
713            count = Math.abs(Math.floor(count));
714            const lastDigit = count % 10;
715            const lastTwoDigits = count % 100;
716            if (lastTwoDigits >= 11 && lastTwoDigits <= 14) return translations.children_5_plus;
717            if (lastDigit === 1) return translations.child;
718            if (lastDigit >= 2 && lastDigit <= 4) return translations.children_2_4;
719            return translations.children_5_plus;
720        };
721
722        const adultsStr = `${adults} ${adultWord(adults)}`;
723        const childrenStr = `${children} ${childWord(children)}`;
724        return `${adultsStr}, ${childrenStr}`;
725    }
726
727    function updateGuestsString() {
728        const adults = parseInt($('.guests-select__stepper-input[name="adults"]').val()) || 0;
729        const children = parseInt($('.guests-select__stepper-input[name="children"]').val()) || 0;
730
731        $('.guests-select_form-input[name="adults"]').val(adults);
732        $('.guests-select_form-input[name="children"]').val(children);
733        const guestsString = getGuestsString(adults, children);
734        $('#guests-result').val(guestsString);
735    }
736
737    $('.guests-select__stepper-button.-plus').on('click', function() {
738        const $input = $(this).siblings('.guests-select__stepper-input');
739        let value = parseInt($input.val()) || 0;
740        const totalGuests = getTotalGuests();
741        if ($input.attr('name') === 'adults') {
742            if (value < MAX_ADULTS && totalGuests < MAX_TOTAL_GUESTS) {
743                value++;
744                $input.val(value);
745                updateGuestsString();
746            }
747        } else if ($input.attr('name') === 'children') {
748            if (totalGuests < MAX_TOTAL_GUESTS) {
749                value++;
750                $input.val(value);
751                updateGuestsString();
752            }
753        }
754    });
755
756    $('.guests-select__stepper-button.-minus').on('click', function() {
757        const $input = $(this).siblings('.guests-select__stepper-input');
758        let value = parseInt($input.val()) || 0;
759
760        if ($input.attr('name') === 'adults') {
761            if (value > MIN_ADULTS) {
762                value--;
763                $input.val(value);
764                updateGuestsString();
765            }
766
767
768        }else if ($input.attr('name') === 'children') {
769
770            if (value > 0) {
771                value--;
772                $input.val(value);
773                updateGuestsString();
774            }
775        }
776    });
777
778    $('.j-close-popup-guest .guests-select__button.-apply').on('click', function() {
779        $('#guests-select-widget').modal('hide');
780    });
781
782    $('.j-close-popup-calendar .calendar-select__button').on('click', function() {
783        $('#calendar-widget').modal('hide');
784    });
785
786    $('.guests-select__close-icon').on('click', function() {
787        $('#guests-select-widget').modal('hide');
788    });
789
790    $('.guests-select__stepper-input').on('input', function() {
791        let value = this.value.replace(/[^0-9]/g, '');
792        if (value === '') value = '0';
793        value = parseInt(value);
794        if (value > 99) value = 99;
795        if (value < 0) value = 0;
796        $(this).val(value);
797    });
798
799    updateGuestsString();
800
801    var $containers = $('.month-container');
802    var totalContainers = $containers.length;
803    var visibleCount = 2;
804    var firstVisibleIndex = 0;
805
806    var $prevButton = $('.j-month-navigation-prev');
807    var $nextButton = $('.j-month-navigation-next');
808
809    function updateDisplay() {
810        if ($containers.length === 0) {
811            return;
812        }
813
814        var $currentVisible = $containers.filter(':visible');
815        var $newVisible = $containers.slice(firstVisibleIndex, firstVisibleIndex + visibleCount);
816
817        $currentVisible.fadeOut(300, function() {
818            $newVisible.fadeIn(300);
819            var isPrevDisabled = firstVisibleIndex === 0;
820            var isNextDisabled = firstVisibleIndex >= totalContainers - visibleCount;
821            $prevButton.toggleClass('disabled', isPrevDisabled);
822            $nextButton.toggleClass('disabled', isNextDisabled);
823        });
824    }
825
826    $prevButton.on('click', function() {
827        if (firstVisibleIndex > 0) {
828            firstVisibleIndex--;
829            updateDisplay();
830        }
831    });
832
833    $nextButton.on('click', function() {
834        if (firstVisibleIndex < totalContainers - visibleCount) {
835            firstVisibleIndex++;
836            updateDisplay();
837        }
838    });
839
840    var intervalId = null;
841    var elapsedSeconds = 0;
842    const UPDATE_SECONDS = 300;
843    const INTERVAL_SECONDS = 5;
844
845    function loadCalendar() {
846        $.ajax({
847            url: '/booking/booking/load-calendar-data',
848            type: 'POST',
849            data: {
850                _csrf: yii.getCsrfToken()
851            },
852            dataType: 'json',
853            success: function(response) {
854                if (response.success) {
855                    updateCalendarDays(response.days);
856                } else {
857                    console.error('Error loading calendar data: ' + response.error);
858                }
859                resetTimer();
860            },
861            error: function(xhr, status, error) {
862                console.error('AJAX error: ' + error);
863                resetTimer();
864            }
865        });
866    }
867
868    function updateCalendarDays(days) {
869        var $container = $('#calendar');
870
871        $.each(days, function(index, day) {
872            var $dayBlock = $container.find('.calendar-day[data-date="' + day.date + '"]');
873
874            if ($dayBlock.length) {
875                $dayBlock.removeClass('before-now enabled disabled disabled-checkin j-calendar-day tooltip-wrapper');
876                $dayBlock.addClass(day.isBeforeNow ? 'before-now' : (day.isAvailable ? 'enabled j-calendar-day tooltip-wrapper' : (day.checkoutOnly ? 'disabled-checkin j-calendar-day' :  'disabled j-calendar-day')));
877
878                var $priceSpan = $dayBlock.find('.calendar-day-price span');
879                if (day.isBeforeNow) {
880                    $priceSpan.html(' ');
881                } else if (day.isAvailable && day.price) {
882                    $priceSpan.text(day.price + ' ₽');
883                } else if(day.checkoutOnly){
884                    $priceSpan.text(translations.checkin_not_available);
885                } else {
886                    $priceSpan.text(translations.not_available);
887                }
888
889                var $tooltip = $dayBlock.find('.tooltip_place');
890                if (day.isAvailable && !day.isBeforeNow) {
891                    if (!$tooltip.length) {
892                        $dayBlock.append($('<span>').addClass('tooltip_place'));
893                    }
894                } else if(day.checkoutOnly){
895                    if (!$tooltip.length) {
896                        $dayBlock.append($('<span>').addClass('tooltip_place'));
897                    }
898                }
899                else {
900                    $tooltip.remove();
901                }
902
903                $dayBlock.attr('data-formatted-date', day.formattedDate);
904            }
905        });
906    }
907
908    function updateProgressBar() {
909        var $progressBar = $('#progress-bar');
910        var progressPercent = (elapsedSeconds / UPDATE_SECONDS) * 100;
911        $progressBar.css('width', progressPercent + '%');
912    }
913
914    function resetTimer() {
915        elapsedSeconds = 0;
916        updateProgressBar();
917    }
918
919    function startTimer() {
920        if (intervalId === null) {
921            intervalId = setInterval(function() {
922                elapsedSeconds += INTERVAL_SECONDS;
923                updateProgressBar();
924                if (elapsedSeconds >= UPDATE_SECONDS) {
925                    loadCalendar();
926                }
927            }, INTERVAL_SECONDS * 1000);
928        }
929    }
930
931    function stopTimer() {
932        if (intervalId !== null) {
933            clearInterval(intervalId);
934            intervalId = null;
935            resetTimer();
936        }
937    }
938    startTimer();
939
940    console.log('Initializing slider');
941    $containers.addClass('hidden');
942    $containers.slice(0, visibleCount).removeClass('hidden');
943    updateDisplay();
944    setCalendarWidgetTop();
945    setGuestWidgetTopLeft();
946});

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.