PageSourceSearch

https://kinoteatr.ru/cinema/js/schedule.js?v=1.18

js kinoteatr.ru collected 2026-09-24 18:20:02 UTC 19,436 bytes, 496 lines download raw bytes

1/*==========================================================================================================================================================================*/
2/* Schedule Navigation */
3
4// Задание атрибута навигации расписания фильма на страницах "Карточка фильма":
5if (document.querySelector(".page-film") && document.querySelector("[data-film-title]")) {
6    var filmTitle = document.querySelector("[data-film-title]").innerText.replaceAll(" ", "");
7    setAttributeNameButton(filmTitle);
8}
9if (document.querySelector(".page-event-some")) {
10    var eventTitle = document.querySelector("[data-event-title]").getAttribute("data-event-title");
11    setAttributeNameButton(eventTitle);
12}
13if (document.querySelector(".page-main")) {
14    setAttributeNameButton("all");
15}
16
17
18function setAttributeNameButton(attributeName) {
19    var scheduleButtons = document.querySelectorAll(".cinema-nav-schedule__date");
20    scheduleButtons.forEach(scheduleButton => {
21        scheduleButton.setAttribute("data-nav", attributeName);
22    });
23}
24
25
26
27/*==========================================================================================================================================================================*/
28/* Class Schedule Navigation */
29class ScheduleNavigation {
30    constructor(props) {
31		if(props.bInit) return;
32        this.navigation = props;
33		this.navigation.bInit = true;
34        this.months = ["Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь"];
35        this.initNavigation();
36    }
37
38
39    /*=================================================================================*/
40    /* Инициализация навигации */
41    initNavigation() {
42        this.scheduleBody = this.navigation.querySelector(".cinema-nav-schedule__body");
43        this.scheduleItems = this.navigation.querySelectorAll(".cinema-nav-schedule__item");
44        this.dateButtons = this.navigation.querySelectorAll(".cinema-nav-schedule__date");
45        this.lineActive = this.navigation.querySelector(".cinema-nav-schedule__line");
46        this.monthBlock = this.navigation.querySelector(".cinema-nav-schedule__months");
47        this.monthElements = this.navigation.querySelectorAll(".cinema-nav-schedule__month");
48        this.activeDate = this.navigation.querySelector("button.active");
49        this.currentDate = this.navigation.querySelector("button.current-date");
50        this.firstDates = this.navigation.querySelectorAll(".first-date");
51        this.lastDates = this.navigation.querySelectorAll(".last-date");
52        this.scheduleListWidth = 0;
53        this.mediaTablet = window.matchMedia("(max-width: 63.99875em)");
54        this.mediaMobile = window.matchMedia("(max-width: 47.99875em)");
55        this.coordsLeft = [];
56        this.valuesPaddingObj = {
57            "pc": [12, 22, 32, 42, 52],
58            "tablet": [30, 40, 49, 58, 66],
59            "mobile": [25, 36, 46, 56, 64],
60        }
61
62        // Объект типов viewport:
63        this.viewports = {
64            pc: false,
65            tablet: false,
66            mobile: false,
67        };
68
69        // Скрыть навигацию, если один элемент даты (скрытый):
70        if (this.dateButtons.length < 2) {
71            this.hideNavigation()
72        } else {
73            // Регистрация событий:
74            this.navigationEvents();
75
76            // Вычисление типа viewport:
77            this.checkOnViewports();
78
79            // Построение элементов дат:
80            this.buildDates();
81
82            // Построение элементов месяцев:
83            this.buildNavigation();
84        }
85    }
86
87
88    /*=================================================================================*/
89    /* Скрытие блока с навигацией, если один элемент даты (скрытый) */
90    hideNavigation() {
91        this.scheduleBody.setAttribute("hidden", "");
92    }
93
94
95    /*=================================================================================*/
96    /* События */
97    navigationEvents() {
98        this.mediaTablet.addEventListener("change", function() {
99            // Сброс значений:
100            this.reset();
101
102            // Вычисление типа viewport:
103            this.checkOnViewports();
104
105            // Построение элементов дат:
106            this.buildDates();
107
108            // Построение навигации:
109            this.buildNavigation();
110        }.bind(this));
111
112
113        this.mediaMobile.addEventListener("change", function() {
114            // Сброс значений:
115            this.reset();
116
117            // Вычисление типа viewport:
118            this.checkOnViewports();
119
120            // Построение элементов дат:
121            this.buildDates();
122
123            // Построение навигации:
124            this.buildNavigation();
125        }.bind(this));
126
127
128        window.addEventListener("orientationchange", function() {
129            // Сброс значений:
130            this.reset();
131
132            // Вычисление типа viewport:
133            this.checkOnViewports();
134
135            // Построение элементов дат:
136            this.buildDates();
137
138            // Построение навигации:
139            this.buildNavigation();
140        }.bind(this));
141
142		 window.addEventListener("resize", function() {
143			 this.addFakeDates();
144		 }.bind(this));
145
146
147        this.dateButtons.forEach(dateButton => {
148			if( dateButton.classList.contains("inactive") ) return;
149            dateButton.addEventListener("click", this.onClickDateButton.bind(this));
150            dateButton.addEventListener("setDateQuiet", this.onChangeDateQuiet.bind(this));
151        });
152    }
153
154
155    /*=================================================================================*/
156    /* Функция вычисления типа viewport */
157    checkOnViewports() {
158        if (this.mediaTablet.matches) {
159            this.viewports.pc = false;
160
161            if (this.mediaMobile.matches) {
162                this.viewports.tablet = false;
163                this.viewports.mobile = true;
164            } else {
165                this.viewports.tablet = true;
166                this.viewports.mobile = false;
167            }
168        } else {
169            this.viewports.pc = true;
170            this.viewports.tablet = false;
171        }
172    }
173
174
175    /*=================================================================================*/
176    /* Построение элементов дат */
177    buildDates() {
178        // Расчет размеров списка даты:
179        this.getScheduleSizes();
180
181        // Работа с элементами последних дат:
182        this.checkLastDates();
183
184        // Переключение видимости боковых теней списка дат:
185        this.toggleShadow();
186    }
187
188
189    /*=================================================================================*/
190    /* Построение элементов месяцев */
191    buildNavigation() {
192        // Если в первом месяце одна дата:
193        this.hideFirstMonth && this.hideMonth();
194
195        // Расчет положения текущей даты:
196        this.calcCoordsLeft();
197
198        // Расстановка элементов месяцев:
199        this.calcSizesMonths();
200
201        this.monthBlock.style.width = `${(this.scheduleBody.scrollWidth / 16)}rem`;
202
203        // Если проставлена текущая дата ==> вызываем функцию расчета позиции нижней линии "активной" даты:
204        if (this.currentDate) {
205            this.setPositionLine();
206        } else {
207            this.lineActive.setAttribute("hidden", "");
208        }
209    }
210
211
212    /*=================================================================================*/
213    /* Функция расчета размеров списка даты */
214    getScheduleSizes() {
215        let dateButtonWidth = this.viewports.pc ? 60 : 52;
216		let bFirstFakeAlreadyFound = false;
217
218		this.scheduleListWidth = 0;
219        this.dateButtons.forEach((dateButton, index) => {
220            let addValue = 0;
221			if (dateButton.parentElement.classList.contains("fake")) {
222				if( !bFirstFakeAlreadyFound ) {
223					bFirstFakeAlreadyFound = true;
224				} else {
225					return;
226				}
227			}
228
229            if (dateButton.classList.contains("last-date")) {
230                addValue = this.viewports.pc ? 30 : 10;
231            } else {
232                if (index !== (this.dateButtons.length - 1)) {
233                    addValue = this.viewports.pc ? 10 : 0;
234                }
235            }
236
237            if (window.innerWidth > 767.98 || (window.innerWidth < 767.98 && index !== 0)) {
238                this.scheduleListWidth += dateButtonWidth + addValue;
239            }
240        });
241    }
242
243
244    /*=================================================================================*/
245    /* Работа с элементами последних дат */
246    checkLastDates() {
247        let viewportKey;
248
249        this.lastDates.forEach((lastDate, index) => {
250            lastDate.parentElement.classList.add("last");
251
252            // Если первая дата в списке (скрываемая) является последней в месяце:
253            if (index === 0 &&
254                (Array.from(this.scheduleItems).indexOf(lastDate.parentElement) === 0)) {
255                this.hideFirstMonth = true;
256            }
257
258            // Если последняя дата является первой, т.е. в месяце единственная дата:
259            if (lastDate.classList.contains("first-date") ||
260            lastDate.classList.contains("current-date") ||
261            (Array.from(this.scheduleItems).indexOf(lastDate.parentElement) === 0)) {
262                const monthElement = this.monthElements[index].querySelector("span");
263
264                // Если в месяце больше 5 букв:
265                if (monthElement.innerText.length > 4) {
266                    const wordsQty = monthElement.innerText.length;
267
268                    for (const key in this.viewports) {
269                        if (this.viewports[key] === true) {
270                            viewportKey = key;
271                            break;
272                        }
273                    }
274
275                    lastDate.parentElement.style.paddingRight = `${(this.valuesPaddingOb
275j[viewportKey][wordsQty - 5]) / 16}rem`;
276                }
277            }
278        });
279    }
280
281
282    /*=================================================================================*/
283    /* Переключение видимости боковых теней списка дат */
284    toggleShadow() {
285        let addValueMargin = this.viewports.mobile ? 24 : 0;
286        if (this.scheduleListWidth > (this.scheduleBody.clientWidth - addValueMargin)) {
287            this.scheduleBody.parentElement.classList.add("scroll");
288        } else {
289            this.scheduleBody.parentElement.classList.remove("scroll");
290        }
291		this.addFakeDates();
292    }
293
294    // Добавление фейковых дат для корректного отображения расписания
295    addFakeDates() {
296		let bChangeState = false;
297		if( this.scheduleListWidth < this.scheduleBody.clientWidth ) {
298			if( !this.scheduleBody.classList.contains('noscroll') ) {
299				this.scheduleBody.classList.add('noscroll');
300				bChangeState = true;
301			}
302		} else {
303			if( this.scheduleBody.classList.contains('noscroll') ) {
304				this.scheduleBody.classList.remove('noscroll');
305				bChangeState = true;
306			}
307		}
308
309		if( bChangeState ) {
310			this.reset();
311			this.checkOnViewports();
312			this.buildDates();
313			this.buildNavigation();
314		}
315	}
316
317
318    /*=================================================================================*/
319    /* Функция скрытия/показа первого месяца навигации */
320    hideMonth() {
321        this.firstMonth = this.navigation.querySelector(".cinema-nav-schedule__month");
322        if (this.viewports.mobile) {
323            this.firstMonth.style.display = "none";
324        } else {
325            this.firstMonth.style.display = "inline-block";
326        }
327    }
328
329
330    /*=================================================================================*/
331    /* Функция расчета положения текущей даты */
332    calcCoordsLeft() {
333		this.coordsLeft = [];
334
335        // Если имеется "активная" дата:
336        if (this.activeDate) {
337            // Исправлено: берем offsetLeft у родительского <li>
338            this.coordsLeft.push(this.activeDate.parentElement.offsetLeft);
339        } else {
340            if (this.viewports.pc) this.coordsLeft.push(70);
341            if (this.viewports.tablet) this.coordsLeft.push(52);
342            if (this.viewports.mobile) this.coordsLeft.push(0);
343        }
344
345        // Если в расписании НЕ 1 месяц:
346        if (this.firstDates.length > 0) {
347            this.firstDates.forEach(firstDate => {
348                // Исправлено: берем offsetLeft у родительского <li>
349                this.coordsLeft.push(firstDate.parentElement.offsetLeft);
350            });
351        }
352    }
353
354
355    /*=================================================================================*/
356    /* Расстановка элементов месяцев */
357    /*=================================================================================*/
358    /* Расстановка элементов месяцев */
359    calcSizesMonths() {
360        let widthMonthElements = 0;
361
362        if (this.firstDates.length === 0) {
363            this.monthElements[0].style.width = `${(this.scheduleBody.scrollWidth / 16)}rem`;
364        }
365
366        this.monthElements.forEach((monthItem, index) => {
367            const monthElement = monthItem.querySelector("span");
368            if( !monthItem.classList.contains("fake") ) {
369				monthElement.style.left = `${this.coordsLeft[0] / 16 }rem`;
370			}
371
372            // Если первый месяц навигации:
373            if (index === 0) {
374
375                // Если в первом месяце единственная дата (скрываемая):
376                if (this.hideFirstMonth) {
377                    monthItem.style.width = `${monthElement.clientWidth / 16}rem`;
378                    monthElement.style.left = 0;
379                }
380
381                if( typeof(this.coordsLeft[index + 1]) == 'undefined' ) {
382					monthItem.style.width = `${(this.monthBlock.clientWidth) / 16}rem`;
383					widthMonthElements = monthItem.clientWidth;
384				} else if( this.coordsLeft[index + 1] != 0 ) {
385                    // Убрали костыль с "- 21.5"
386					monthItem.style.width = `${(this.coordsLeft[index + 1]) / 16}rem`;
387					widthMonthElements = this.coordsLeft[index + 1];
388				} else {
389					monthItem.style.width = "100%";
390					widthMonthElements = monthItem.clientWidth;
391				}
392
393            // Если НЕ первый месяц навигации:
394            } else {
395                // Если НЕ последний месяц навигации:
396                if (index < (this.monthElements.length - 1)) {
397                    // Убрали костыли с "(20 + 1.5 * (index + 1))"
398                    let monthWidth = this.coordsLeft[index + 1] - this.coordsLeft[index];
399                    monthItem.style.width = `${monthWidth / 16}rem`;
400                    widthMonthElements += monthWidth;
401                // Если последний месяц:
402                } else {
403                    monthItem.style.width = `${(this.scheduleBody.scrollWidth - widthMonthElements) / 16}rem`;
404                }
405            }
406        });
407    }
408
409
410    /*=================================================================================*/
411    /* Функция расчета позиции линии "активной" даты */
412    setPositionLine() {
413        var addValue = this.viewports.pc ? 6 : 7;
414        // Исправлено: берем offsetLeft у родительского <li>
415        this.lineActive.style.left = `${(this.currentDate.parentElement.offsetLeft + addValue) / 16}rem`;
416    }
417
418
419    /*=================================================================================*/
420    /* Функция сброса значений */
421    reset() {
422        this.coordsLeft = [];
423        this.monthBlock.style.width = "";
424
425        this.monthElements.forEach(month => {
426            month.style.width = "";
427            month.querySelector("span").style.left = "";
428        });
429
430        this.lastDates.forEach(date => {
431            date.parentElement.style.paddingRight = "";
432        });
433    }
434
435
436    /*=================================================================================*/
437    /* Функция-обработчик "клика" на кнопку даты */
438    onClickDateButton(e) {
439        if (e.target.closest(".dragging")) {
440            e.preventDefault();
441            return false;
442        } else {
443            const targetElement = e.currentTarget;
444
445            if (!targetElement.classList.contains("active")) {
446                this.activeDate = this.navigation.querySelector(".cinema-nav-schedule__date.active");
447
448                if (this.activeDate) {
449                    this.activeDate.classList.remove("active");
450                }
451
452                targetElement.classList.add("active");
453            }
454            // if(typeof(app) != 'undefined') app.LoadDateSchedule(targetElement);
455            /* Новый блок события */
456            if(this.navigation.dataset?.eventEmitSelector) {
457                const elem = document.querySelector(this.navigation.dataset?.eventEmitSelector);
458                const date = targetElement.dataset?.date;
459                if( elem && date ) elem.dispatchEvent( new CustomEvent('setDate', { detail: { date } }) );
460            }
461            /* Конец: Новый блок события */
462        }
463    }
464
465    onChangeDateQuiet(e) {
466        // console.log('onChangeDateQuiet', e.currentTarget);
467        const targetElement = e.currentTarget;
468        if( !targetElement ) return;
469        if (!targetElement.classList.contains("active")) {
470            this.activeDate = this.navigation.querySelector(".cinema-nav-schedule__date.active");
471
472            if (this.activeDate) {
473                this.activeDate.classList.remove("active");
474            }
475
476            targetElement.classList.add("active");
477        }
478
479    }
480}
481
482
483// Запуск конструктора ScheduleNavigation:
484function initSchedule(){
485    let scheduleItems = [];
486    if (scheduleItems = document.querySelectorAll(".cinema-nav-schedule")) {
487        scheduleItems.forEach(scheduleItem => {
488            if(scheduleItem.classList.contains("_js_schedule_activated")) return;
489            new ScheduleNavigation(scheduleItem);
490            scheduleItem.classList.add("_js_schedule_activated");
491        });
492
493    }
494}
495
496window.addEventListener("DOMContentLoaded", initSchedule);

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.