PageSourceSearch

https://ticket-khl.ru/local/components/tickets/filter_d/templates/.default/script.js

js ticket-khl.ru collected 2026-10-08 13:51:35 UTC 32,520 bytes, 793 lines download raw bytes

1document.addEventListener('DOMContentLoaded', () => {
2    const appElement = document.getElementById('app');
3    const initialEvents = JSON.parse(appElement.dataset.initialEvents || '[]');
4    const initialFilter = JSON.parse(appElement.dataset.initialFilter || '{}');
5    const showMore = appElement.dataset.showMore === 'true';
6
7    const { createApp } = Vue;
8
9    createApp({
10      data() {
11        return {
12          cityFilter: {
13            defaultFilterText: "Все города",
14            multipleChoiceText: "Выбрано городов ",
15            filterText: "Все города",
16            data: [],
17          },
18          locationFilter: {
19            defaultFilterText: "Все стадионы",
20            multipleChoiceText: "Выбрано стадионов ",
21            filterText: "Все стадионы",
22            data: [],
23          },
24          eventTypesFilter: {
25            defaultFilterText: "Все категории",
26            multipleChoiceText: "Выбрано категорий ",
27            filterText: "Все категории",
28            data: [],
29          },
30          liguesFilter: {
31            defaultFilterText: "Все лиги",
32            multipleChoiceText: "Выбрано лиг ",
33            filterText: "Все лиги",
34            data: [],
35          },
36          date: {
37            defaultText: { from: "Период от", to: "до" }, // текст по умолчанию для фильтров даты
38            text: { from: "Период от", to: "до" },
39            data: { from: "", to: "" },
40          },
41          events: initialEvents,
42          page: 1,
43          showSelects: {
44            CITY: true,
45            LOCATION: true,
46            EVENT_TYPE: true,
47            LIGA: false,
48          },
49          showTestEvents: false,
50          noEvents: false,
51          saveFilterObj: {}, // объект для сохранения того фильтра, по которому происходит поиск
52          searchInput: "", // тут хранятся данные о текстовом поле поиска
53          filterResetButtonEnable: false, // переключатель активности кнопки сброса фильтров
54          typingTimer: null, // таймер для отслеживания ввода в текстовое поле поиска
55          showMoreButtonEnable: showMore, // свойство контролирует показывать ли кнопку "ПОКАЗАТЬ ЕЩЁ"
56          toAllEventsButtonEnable: false, // свойство контролирует показывать ли кнопку "КО ВСЕМ МЕРОПРИЯТИЯМ"
57          checkFirstClickToDatepicker: true, // переменная для отслеживания первого нажатия на календарь на новом дизайне
58          eventsNotFound: false, // показывать\скрывать сообщение о том, что событий не найдено
59          preLoader: false, // ВКЛ\ВЫКЛ отображение прелоудера
60          dateRange: {
61            // текст на фильтре по датам на новом дизайне
62            defaultText: "Выберите даты", // текст по умолчанию для фильтра даты нового дизайне
63            text: "Выберите даты",
64          },
65        };
66      },
67      methods: {
68        // ОПРАВЛЯЮ ДАННЫЕ ФИЛЬТРА В PHP И ПРИНИМАЮ ДАННЫЕ ОБРАТНО
69        fetchEvents() {
70          if (document.getElementById("bx-panel-top"))
71            this.showTestEvents = true;
72
73          this.preLoader = true;
74          data = this.getFilterData();
75          // кладу в данные информацию о адресе той страницы с которой происходит запрос
76          data.LOCATION_HREF = window.location.href;
77
78          // console.log('ajax query!');
79          // console.log(data);
80
81          $.ajax({
82            url: "/api/controllers/filter/get/",
83            type: "POST",
84            data: data,
85            success: (res) => {
86              try {
87                if (res.status == "success") res = res.data[0];
88                else return false;
89
90                this.drawFilterForm(res.FILTER);
91                this.preLoader = false;
92                this.showMoreButtonEnable = res.SHOW_MORE_BUTTON;
93                this.noEvents = res.NO_EVENT;
94                this.events = res.EVENTS;
95                this.showSelects = res.SHOW_SELECTS;
96
97                // console.log( res )
98                // console.log( '---------' )
99              } catch (error) {
100                console.error("Ошибка", error.message);
101                console.log(res);
102              }
103            },
104            error: function (xhr, status, error) {
105              console.error("Ошибка AJAX:", error);
106            },
107          });
108        },
109
110        getSelectData(target) {
111          if (Object.keys(target).length === 0) {
112            return [];
113          } else {
114            return target
115              .filter((data) => data.selected) // Фильтруем объекты, где selected равно true
116              .map((data) => data.value); // Извлекаем значения value
117          }
118        },
119
120        getFilterData() {
121          return {
122            CITY: this.getSelectData(this.cityFilter.data),
123            LOCATION: this.getSelectData(this.locationFilter.data),
124            CATEGORY: this.getSelectData(this.eventTypesFilter.data),
125            LIGA: this.getSelectData(this.liguesFilter.data),
126            Q: this.searchInput,
127            DATE_FROM: this.date.data.from,
128            DATE_TO: this.date.data.to,
129            PAGE: this.page,
130            SHOW_TEST: this.showTestEvents,
131          };
132        },
133
134        // РИСУЮ ФОРМУ ФИЛЬТРА
135        drawFilterForm(data) {
136          // console.log('draw filter form')
137          // console.log(data)
138          this.cityFilter.data = data.CITY;
139          this.locationFilter.data = data.LOCATION;
140          this.eventTypesFilter.data = data.EVENT_TYPE;
141          this.liguesFilter.data = data.LIGA;
142
143          // если есть сохранённая активная форма, то присваиваем её нужному объекту
144          if (JSON.stringify(this.saveFilterObj) !== "{}") {
145            [
146              this.cityFilter,
147              this.locationFilter,
148              this.eventTypesFilter,
149              this.liguesFilter,
150            ].forEach((filter, index) => {
151              if (
152                this.saveFilterObj.defaultFilterText == filter.defaultFilterText
153              )
154                Object.assign(filter, this.saveFilterObj);
155            });
156          }
157
158          [
159            this.cityFilter,
160            this.locationFilter,
161            this.eventTypesFilter,
162            this.liguesFilter,
163          ].forEach((filter, index) => {
164            this.writeTextOnFilter(filter);
165          });
166
167          // если данных не пришло, то инициализировать datepicker не стоит
168          if (Array.isArray(data.DATE.DATA) && data.DATE.DATA.length !== 0)
169            this.initDatepickers(
170              data.DATE.DATE_MAX,
171              data.DATE.DATE_MIN,
172              data.DATE.DATA.map((dateTime) => dateTime.split(" ")[0]),
173            );
174
175          this.eventsNotFound = this.locationFilter.data.length === 0;
176
177          if (window.location.pathname == "/")
178            this.toAllEventsButtonEnable = true;
179
180          this.changeUrl();
181        },
182
183        // ОТСЛЕЖИВАЮ КЛИК НА ЭЛЕМЕНТ ФИЛЬТРА
184        selectorClick(e) {
185          const exceptionArray = [
186            "ui-icon-circle-triangle-w",
187            "ui-icon-circle-triangle-e",
188          ];
189          if (
190            exceptionArray.some((str) => e.target.outerHTML.includes(str)) &&
191            !["search-form__selector--date-range"].some((str) =>
192              e.target.outerHTML.includes(str),
193            )
194          ) {
195            return false;
196          }
197          // показать\убрать окно выбора в фильтре
198          const clickedListElement = e.currentTarget.querySelector(
199            ".search-form__selector-list",
200          );
201          // добавляю скролл, если элементов в фильтре > 5
202          if (
203            clickedListElement.querySelectorAll(".search-form__selector-option")
204              .length > 5
205          ) {
206            clickedListElement.classList.add("scroll");
207          }
208          // убираю отображение фильра у всех, кроме того на который кликнули
209          document
210            .querySelectorAll("div.search-form__selector-list")
211            .forEach((element) => {
212              if (element !== clickedListElement)
213                element.classList.remove("show");
214            });
215          clickedListElement.classList.toggle("show");
216        },
217
218        // ПОКАЗАТЬ СКРЫТЫЕ ФИЛЬТРЫ В МОБИЛЬНОЙ ВЕРСИИ НОВОГО ДИЗАЙНА
219        shoeMoreButton(e) {
220          if (e.target.classList.contains("show")) {
221            e.target.classList.remove("show");
222            document
223              .querySelector(".search-form__row-select")
224              .classList.remove("show");
225          } else {
226            e.target.classList.add("show");
227            document
228              .querySelector(".search-form__row-select")
229              .classList.add("show");
230          }
231        },
232
233        // ОБРАБОТЧИК НАВЕШАННЫЙ В HTML
234        selectFilter(pieceObj, fullObj) {
235          if (!pieceObj) {
236            fullObj.data.forEach((data) => (data.selected = false));
237            this.saveFilterObj = {};
238          } else {
239            pieceObj.selected = !pieceObj.selected;
240            this.saveFilterObj = Object.assign({}, fullObj);
241          }
242          this.fetchEvents();
243        },
244
245        // ЛОГИКА СМЕНЫ ТЕКСТА НА ФИЛЬТРЕ
246        writeTextOnFilter(target) {
247          let selectedCount = target.data.filter(
248            (data) => data.selected,
249          ).length;
250          if (selectedCount == 0) {
251            target.filterText = target.defaultFilterText;
252          } else if (selectedCount == 1) {
253            target.filterText = target.data.find((data) => data.selected).name;
254          } else {
255            target.filterText =
256              target.multipleChoiceText + selectedCount + "шт.";
257          }
258
259          if (target.data.length == 1) target.filterText = target.data[0].name;
260        },
261
262        onSearchInputChange() {
263          // Очистка предыдущего таймера, если он был задан
264          clearTimeout(this.typingTimer);
265          // Установка нового таймера
266          this.typingTimer = setTimeout(() => {
267            if (this.searchInput.trim() == "") this.searchInput = "";
268            this.fetchEvents();
269          }, 1000);
270        },
271
272        checkAllSelectedFalse(target) {
273          return (allSelectedFalse = !target.every(
274            (city) => city.selected === false,
275          ));
276        },
277
278        checkFiltersFull() {
279          return (
280            this.checkAllSelectedFalse(this.cityFilter.data) ||
281            this.checkAllSelectedFalse(this.locationFilter.data) ||
282            this.checkAllSelectedFalse(this.eventTypesFilter.data) ||
283            this.checkAllSelectedFalse(this.liguesFilter.data) ||
284            this.searchInput.trim() !== "" ||
285            this.date.data.from !== "" ||
286            this.date.data.to !== "" ||
287            this.page > 1
288          );
289        },
290
291        resetButtonSwitch() {
292          this.filterResetButtonEnable = this.checkFiltersFull();
293        },
294
295        resetFilter() {
296          if (this.filterResetButtonEnable && this.checkFiltersFull()) {
297            this.filterResetButtonEnable = false;
298            this.page = 1;
299            this.checkFirstClickToDatepicker = true;
300            this.dateRange.text = this.dateRange.defaultText;
301            this.saveFilterObj = {};
302            this.searchInput = "";
303            this.date.data.from = "";
304            this.date.data.to = "";
305            this.date.text.from = this.date.defaultText.from;
306            this.date.text.to = this.date.defaultText.to;
307            this.cityFilter.filterText = this.cityFilter.defaultFilterText;
308            this.locationFilter.filterText =
309              this.locationFilter.defaultFilterText;
310            this.eventTypesFilter.filterText =
311              this.eventTypesFilter.defaultFilterText;
312            this.liguesFilter.filterText = this.liguesFilter.defaultFilterText;
313            this.cityFilter.data.forEach((city) => {
314              city.selected = false;
315            }); // Устанавливаем у городов поле selected в false
316            this.locationFilter.data.forEach((location) => {
317              location.selected = false;
318            }); // Устанавливаем у стадионов поле selected в false
319            this.eventTypesFilter.data.forEach((eventType) => {
320              eventType.selected = false;
321            }); // Устанавливаем у категорий поле selected в false
322            this.liguesFilter.data.forEach((ligue) => {
323              ligue.selected = false;
324            }); // Устанавливаем у лиг поле selected в false
325            this.fetchEvents();
326          }
327        },
328
329        // ОТРАБАТЫВАЮ НАЖАТИЕ НА ДОКУМЕНТ ВНЕ САМИХ ФИЛЬТРОВ
330        clickOutsideFilters(e) {
331          const isClickInside = e.target.closest(".search-form__row-select");
332          var classAttr = $(e.target).attr("class");
333          if (!isClickInside && classAttr && classAttr.indexOf("ui-") < 0) {
334            document
335              .querySelectorAll(".search-form__selector-list")
336              .forEach((element) => {
337                element.classList.remove("show");
338              });
339          }
340        },
341
342        // ДОБАВЛЯЕМ В АДРЕСНУЮ СТРОКУ ДАННЫЕ ИЗ ФИЛЬТРА
343        changeUrl() {
344          let url = window.location,
345            params = new URLSearchParams(window.location.search),
346            data = this.getFilterData();
347
348          if (params.has("PAGE")) params.delete("PAGE");
349          if (data.PAGE == 1) delete data.PAGE;
350
351          if (params.has("SHOW_TEST")) params.delete("SHOW_TEST");
352          delete data.SHOW_TEST;
353
354          for (const key in data) {
355            if (data[key].length == 0 || params.has(key)) params.delete(key);
356            if (data[key].length != 0) params.append(key, data[key]);
357          }
358
359          const new_url = new URL(
360            `${url.origin}${url.pathname}?${params.toString()}`,
361          );
362          // тут убираем знако вопроса в конце строки, если он появляется
363          if (new_url.href.endsWith("?"))
364            new_url.href = new_url.href.slice(0, -1);
365          history.pushState({ url: url.search }, null, new_url.href);
366        },
367
368        // ФИЛЬТРАЦИЯ ЭЛЕМЕНТОВ ОПИРАЯСЬ НА ПАРАМЕТРЫ ИЗ URL
369        filterByUrl() {
370          let checkUrlParam = false,
371            url = new URL(window.location.href),
372            keys = url.searchParams.keys();
373
374          Array.from(keys).forEach((key) => {
375            if (
376              [
377                "CITY",
378                "LOCATION",
379                "CATEGORY",
380                "LIGA",
381                "Q",
382                "DATE_FROM",
383                "DATE_TO",
384                "PAGE",
385              ].includes(key)
386            ) {
387              let value = url.searchParams.get(key).split(",");
388              for (const res of value) {
389                if (key == "CITY")
390                  this.cityFilter.data.push({
391                    name: res,
392                    value: res,
393                    selected: true,
394                  });
395                if (key == "LOCATION")
396                  this.locationFilter.data.push({
397                    name: res,
398                    value: res,
399                    selected: true,
400                  });
401                if (key == "CATEGORY")
402                  this.eventTypesFilter.data.push({
403                    name: res,
404                    value: res,
405                    selected: true,
406                  });
407                if (key == "LIGA")
408                  this.liguesFilter.data.push({
409                    name: res,
410                    value: res,
411                    selected: true,
412                  });
413                if (key == "Q") this.searchInput = res;
414
415                if (key == "PAGE") this.page = res;
416
417                if (key == "DATE_FROM") {
418                  this.date.text.from = this.formatDate(res);
419                  this.date.data.from = res;
420                }
421                if (key == "DATE_TO") {
422                  this.date.text.to = this.formatDate(res);
423                  this.date.data.to = res;
424                }
425                checkUrlParam = true;
426              }
427            }
428          });
429
430          if (this.date.data.from && this.date.data.to) {
431            this.dateRange.text = this.date.text.from + " - " + this.date.text.to;
432          }
433
434          return !checkUrlParam;
435        },
436
437        formatDate(dateString) {
438          const parts = dateString.split("-");
439          return `${parts[2]}.${parts[1]}.${parts[0]}`;
440        },
441
442        showMoreButton() {
443          this.page = this.page + 1;
444          this.fetchEvents();
445        },
446
447        toAllEventsButton(e) {
448          let url = window.location,
449            params = new URLSearchParams(window.location.search),
450            data = this.getFilterData();
451
452          for (const key in data) {
453            if (data[key].length == 0 || params.has(key)) params.delete(key);
454            if (data[key].length != 0) params.append(key, data[key]);
455          }
456
457          if (params.has("PAGE")) params.delete("PAGE");
458
459          if (params.has("SHOW_TEST")) params.delete("SHOW_TEST");
460
461          const new_url = new URL(
462            `${url.origin}/${e.currentTarget.getAttribute("data-iss")}/?${params.toString()}`,
463          );
464          // тут убираем знако вопроса в конце строки, если он появл
464яется
465          if (new_url.href.endsWith("?"))
466            new_url.href = new_url.href.slice(0, -1);
467
468          window.location.href = new_url.href;
469        },
470
471        fastDateFilter(e) {
472          this.date.data.from = e.target.dataset.start;
473          this.date.data.to = e.target.dataset.end;
474          this.date.text.from = this.formatDate(e.target.dataset.start);
475          this.date.text.to = this.formatDate(e.target.dataset.end);
476          this.checkFirstClickToDatepicker = true;
477          this.dateRange.text =
478            this.date.text.from + " - " + this.date.text.to;
479          this.fetchEvents();
480        },
481
482        // Преобразует дату фильтра из YYYY-MM-DD в локальный объект Date.
483        parseFilterDate(dateString) {
484          const [year, month, day] = dateString.split("-").map(Number);
485          return new Date(year, month - 1, day);
486        },
487
488        // Получает дату из DOM-ячейки, которую сгенерировал jQuery UI Datepicker.
489        getDatepickerCellDate(cell) {
490          const day = Number(cell.querySelector("a")?.textContent);
491          const year = Number(cell.dataset.year);
492          const month = Number(cell.dataset.month);
493
494          if (!day || Number.isNaN(year) || Number.isNaN(month)) return null;
495
496          return new Date(year, month, day);
497        },
498
499        // Расставляет CSS-классы начала, конца и промежутка диапазона.
500        // previewDate используется для предпросмотра диапазона до второго клика.
501        paintDateRange(datepickerRange, previewDate = null) {
502          const cells = datepickerRange.find("td[data-handler='selectDay']");
503          cells.removeClass("dp-highlight dp-range-start dp-range-end");
504
505          if (!this.date.data.from) return;
506
507          const startDate = this.parseFilterDate(this.date.data.from);
508          const endDate = previewDate ||
509            (this.date.data.to
510              ? this.parseFilterDate(this.date.data.to)
511              : startDate);
512          const rangeStart = Math.min(startDate.getTime(), endDate.getTime());
513          const rangeEnd = Math.max(startDate.getTime(), endDate.getTime());
514
515          cells.each((index, cell) => {
516            const cellDate = this.getDatepickerCellDate(cell);
517            if (!cellDate) return;
518
519            const cellTime = cellDate.getTime();
520            if (cellTime >= rangeStart && cellTime <= rangeEnd)
521              cell.classList.add("dp-highlight");
522            if (cellTime === startDate.getTime())
523              cell.classList.add("dp-range-start");
524            if (
525              cellTime === endDate.getTime() &&
526              endDate.getTime() !== startDate.getTime()
527            )
528              cell.classList.add("dp-range-end");
529          });
530        },
531
532        initDatepickers(dateMax, dateMin, datesWithEvents) {
533          var datepickerContainer = $(".search-form__selector-list--date"),
534            datepickerStart = $(".search-form__date-selector--start"),
535            datepickerEnd = $(".search-form__date-selector--end"),
536            datepickerRange = $(".search-form__date-selector--range"),
537            activeFilterTextColor = "#233550";
538
539          // если datepicker-ы уже инициализированы, то убиваю их
540          if ($(datepickerStart).datepicker("option") !== undefined)
541            datepickerStart.datepicker("destroy");
542          if ($(datepickerEnd).datepicker("option") !== undefined)
543            datepickerEnd.datepicker("destroy");
544          if ($(datepickerRange).datepicker("option") !== undefined)
545            datepickerRange.datepicker("destroy");
546
547          let startSettings = {
548            regional: "ru",
549            minDate: dateMin,
550            maxDate: dateMax,
551            beforeShowDay: this.beforeShowDay(datesWithEvents, "from"),
552            onSelect: (date, datepicker) => {
553              datepickerStart.datepicker("setDate", date);
554              // форматируем полученную дату в нужном для Битрикс формат
555              let selectedDate = new Date(
556                datepicker.selectedYear,
557                datepicker.selectedMonth,
558                datepicker.selectedDay,
559              );
560              this.date.data.from = $.datepicker.formatDate(
561                "yy-mm-dd",
562                selectedDate,
563              );
564              // тригерим функцию принудительно, так как beforeUpdate() не тригерится при изменении данных тут
565              this.resetButtonSwitch();
566              // меняем надпись в форме
567              this.date.text.from = date;
568              datepickerEnd.datepicker(
569                "option",
570                "minDate",
571                new Date(
572                  datepicker.selectedYear,
573                  datepicker.selectedMonth,
574                  datepicker.selectedDay,
575                ),
576              );
577              datepickerContainer.removeClass("show");
578              // когда выбирается/меняется текст начальной даты в фильтре, то текст в поле выбора даты меняется на другой
579              $(".search-form__selector--start-date").css(
580                "color",
581                activeFilterTextColor,
582              );
583              this.fetchEvents();
584            },
585          };
586
587          let endSettings = {
588            regional: "ru",
589            minDate: dateMin,
590            maxDate: dateMax,
591            beforeShowDay: this.beforeShowDay(datesWithEvents, "to"),
592            onSelect: (date, datepicker) => {
593              datepickerEnd.datepicker("setDate", date);
594              // форматируем полученную дату в нужном для Битрикс формат
595              let selectedDate = new Date(
596                datepicker.selectedYear,
597                datepicker.selectedMonth,
598                datepicker.selectedDay,
599              );
600              this.date.data.to = $.datepicker.formatDate(
601                "yy-mm-dd",
602                selectedDate,
603              );
604              // меняем надпись в форме
605              this.date.text.to = date;
606              // тригерим функцию принудительно, так как beforeUpdate() не тригерится при изменении данных тут
607              this.resetButtonSwitch();
608              datepickerStart.datepicker(
609                "option",
610                "maxDate",
611                new Date(
612                  datepicker.selectedYear,
613                  datepicker.selectedMonth,
614                  datepicker.selectedDay,
615                ),
616              );
617              datepickerContainer.removeClass("show");
618              // когда выбирается/меняется текст конечной даты в фильтре, то текст в поле выбора даты меняется на другой
619              $(".search-form__selector--end-date").css(
620                "color",
621                activeFilterTextColor,
622              );
623              this.fetchEvents();
624            },
625          };
626
627          let rangeSettings = {
628            regional: "ru",
629            minDate: dateMin,
630            maxDate: dateMax,
631            numberOfMonths: 2,
632            beforeShowDay: this.beforeShowDayRange(datesWithEvents),
633            onSelect: (date, datepicker) => {
634              let selectedDate = new Date(
635                datepicker.selectedYear,
636                datepicker.selectedMonth,
637                datepicker.selectedDay,
638              );
639              const selectedDateValue = $.datepicker.formatDate(
640                "yy-mm-dd",
641                selectedDate,
642              );
643
644              if (this.checkFirstClickToDatepicker) {
645                this.date.data.from = selectedDateValue;
646                this.date.text.from = date;
647                this.date.data.to = "";
648                this.date.text.to = this.date.defaultText.to;
649                this.checkFirstClickToDatepicker = false;
650                this.resetButtonSwitch();
651                // Datepicker перерисовывает DOM после onSelect, поэтому ждём завершения перерисовки.
652                setTimeout(() => this.paintDateRange(datepickerRange), 0);
653              } else {
654                const firstDateValue = this.date.data.from;
655                const firstDateText = this.date.text.from;
656
657                if (selectedDate < this.parseFilterDate(firstDateValue)) {
658                  this.date.data.from = selectedDateValue;
659                  this.date.text.from = date;
660                  this.date.data.to = firstDateValue;
661                  this.date.text.to = firstDateText;
662                } else {
663                  this.date.data.to = selectedDateValue;
664                  this.date.text.to = date;
665                }
666
667                this.checkFirstClickToDatepicker = true;
668                this.dateRange.text =
669                  this.date.text.from + " - " + this.date.text.to;
670                datepickerContainer.removeClass("show");
671                this.fetchEvents();
672              }
673            },
674            onChangeMonthYear: () => {
675              setTimeout(() => this.paintDateRange(datepickerRange), 0);
676            },
677            selectOtherMonths: true,
678          };
679
680          datepickerStart.datepicker(startSettings).val("");
681          datepickerEnd.datepicker(endSettings).val("");
682          datepickerRange.datepicker(rangeSettings).val("");
683
684          datepickerRange
685            .off(".dateRangePreview")
686            .on(
687              "mouseenter.dateRangePreview",
688              "td[data-handler='selectDay']",
689              (event) => {
690                if (this.checkFirstClickToDatepicker) return;
691
692                const previewDate = this.getDatepickerCellDate(
693                  event.currentTarget,
694                );
695                if (previewDate)
696                  this.paintDateRange(datepickerRange, previewDate);
697              },
698            )
699            .on(
700              "mouseleave.dateRangePreview",
701              ".ui-datepicker-calendar",
702              () => {
703                if (!this.checkFirstClickToDatepicker)
704                  this.paintDateRange(datepickerRange);
705              },
706            );
707
708          if (this.date.data.from)
709            datepickerStart.datepicker("setDate", this.date.data.from);
710          if (this.date.data.to)
711            datepickerEnd.datepicker("setDate", this.date.data.to);
712          if (this.date.data.from)
713            datepickerRange.datepicker("setDate", this.date.data.from);
714
715          // Накладываем состояние диапазона после полной инициализации календарей.
716          setTimeout(() => this.paintDateRange(datepickerRange), 0);
717        },
718
719        // ФУНКЦИЯ ДЛЯ ВЫДЕЛЕНИЯ ДНЕЙ В КАЛЕНДАРЕ, КОТОРОЙ НУЖНО СКОРМИТЬ МАССИВ С ЭЛЕМЕНТАМИ ВИДА [11.07.2025]
720        beforeShowDay(datesWithEvents, dateField) {
721          return (date) => {
722            let className = "";
723            var dateString = $.datepicker.formatDate("dd.mm.yy", date);
724            if (datesWithEvents.includes(dateString)) {
725              className = "calendar_hasdate";
726            }
727            // jQuery UI сам выделяет minDate, пока дата не выбрана, — гасим это выделение
728            if (!this.date.data[dateField]) {
729              className += " calendar_first";
730            }
731            return [true, className];
732          };
733        },
734
735        beforeShowDayRange(datesWithEvents) {
736          return (date) => {
737            let className = "",
738              dateString = $.datepicker.formatDate("dd.mm.yy", date);
739
740            if (datesWithEvents.includes(dateString))
741              className = "calendar_hasdate";
742
743            if (this.date.data.from) {
744              const from = this.parseFilterDate(this.date.data.from);
745              const to = this.date.data.to
746                ? this.parseFilterDate(this.date.data.to)
747                : from;
748              const dateTime = date.getTime();
749
750              if (dateTime >= from.getTime() && dateTime <= to.getTime())
751                className += " dp-highlight";
752              if (dateTime === from.getTime())
753                className += " dp-range-start";
754              if (dateTime === to.getTime() && to.getTime() !== from.getTime())
755                className += " dp-range-end";
756            }
757
758            return [true, className];
759          };
760        },
761      },
762
763      mounted() {
764        document.querySelector(".event-cards.preloaded")?.remove();
765       
766        document.querySelectorAll(".no-events").forEach((el) => {
767          el.style.display = ""; 
768        });
769
770        document.querySelectorAll(".event__elem--show-more").forEach((el) => {
771          el.style.display = "";
772        });
773
774        if (window.__INITIAL__?.events?.length) {
775          // Синхронизация состояния фильтров
776          this.filterByUrl();
777
778          this.drawFilterForm(window.__INITIAL__.filter);
779          this.showMoreButtonEnable = window.__INITIAL__.showMore;
780          this.noEvents = window.__INITIAL__.noEvents;
781          this.events = window.__INITIAL__.events;
782        } else {
783          this.fetchEvents(this.filterByUrl());
784        }
785
786        document.addEventListener("click", this.clickOutsideFilters);
787      },
788
789      beforeUpdate() {
790        this.resetButtonSwitch(); // При обновлении модели запускаю логику
791      },
792    }).mount("#app");
793});

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.