PageSourceSearch

https://uristy.ru/min/20260925082428/js/utils/player_new.js

js uristy.ru collected 2026-10-04 14:56:39 UTC 33,243 bytes, 1,099 lines download raw bytes

1/**
2 * всякое разное для нашего плеера и наших видосиков, засунул сюда. Кто не согласен - идите нафиг:)
3 */
4
5/**
6 * Конфигурация плеера
7 * @type Object
8 */
9let player_config = {
10	muted: false,
11	volume: 1
12};
13
14let playerFirstInit = true;
15
16/**
17 * текущее воспроизводимое видео
18 * @type type
19 */
20let currentVideo = null;
21
22/**
23 * массив с видосами (без ссылок на видео файл)
24 * @type Array
25 */
26const videos = [];
27
28/**
29 * массив со ссылками на видео
30 * @type Array
31 */
32const videos_src = [];
33
34class Player {
35
36	/**
37	 * объект видоса
38	 */
39	video = null;
40
41	/**
42	 * Идентификатор видоса
43	 * @type Number
44	 */
45	video_id = 0;
46
47	/**
48	 * дефолтное значение звука
49	 * @type Boolean
50	 */
51	muted_default = true;
52
53	/**
54	 * автостарт видосов
55	 * @type Boolean
56	 */
57	autoplay = false;
58
59	/**
60	 * имя обрабатываемого события
61	 * @type String
62	 */
63	event_name = 'click';
64
65	/**
66	 *
67	 * @param {Number} video_id
68	 * @returns {Player}
69	 */
70	constructor(video_id = 0) {
71		this.video_id = video_id;
72
73		if (ru9111.mobile === true) {
74			this.event_name = 'touchend';
75		}
76		//console.log(player_config.volume, player_config.muted);
77	}
78
79	/**
80	 * получаем шаблон плеера
81	 * @param {String} video_url
82	 * @returns {String}
83	 */
84	getTemplate(video_url = '') {
85		if (this.getContainer().length !== 0) {
86			return;
87		}
88		let player_volume = '';
89		if (ru9111.mobile === false) {
90			player_volume = `
91				<div class="player_controls__volume">
92					<div class="player_controls__volume__progress"></div>
93				</div>`;
94		}
95
96		return `
97			<div class="player" id="player${this.video_id}" data-video-id="${this.video_id}" data-fullscreen="false">
98				<div class="player_preloader player_preloader__hide">
99					<div class="preloader__ring preloader__ring__green"><div></div><div></div><div></div><div></div></div>
100				</div>
101				<div class="player__video__share__wrap player__video__share__hide">
102					<div class="player__video__share">
103						<div class="player__video__share__text">Поделиться видео</div>
104						<div class="player__video__share__icon"><svg><use href="#share"></use></svg></div>
105					</div>
106				</div>
107				<div class="player__video_preview" style="background-image:url(${video_url}.jpg);"></div>
108				<video playsinline preload="metadata" src="${video_url}.mp4" type="video/mp4;"></video>
109				<div class="player_controls__wrapper">
110					<div class="player_controls">
111						<div class="player_controls__play"><svg><use href="#video2"></use></svg></div>
112						<div class="player_controls__bar">
113							<div class="player_controls__timeline"></div>
114							<div class="player_controls__buffer"></div>
115							<div class="player_controls__substrate"></div>
116						</div>
117						<div class="player_controls__timer">0:00</div>
118						<div class="player_controls__sound">
119							<div class="player_controls__muted">
120								<svg><use href="#muted_on"></use></svg>
121							</div>
122							${player_volume}
123						</div>
124						<div class="player_controls__fullscreen">
125							<svg><use href="#fullWindow"></use></svg>
126						</div>
127					</div>
128				</div>
129			</div>
130		`;
131	}
132
133	getContainer() {
134		return $('#player'+this.video_id);
135	}
136
137	initPlayer() {
138		let container = this.getContainer();
139
140		this.video = container.find('video')[0];
141
142		let video = this.video;
143		video.id = this.video_id;
144		video.autoplay = site_vars.player_autoplay;
145		video.showAds = false;
146
147		this.video.muted = player_config.muted;
148		if (this.video.muted === true) {
149			this.video.volume = 0;
150		} else {
151			this.video.volume = player_config.volume;
152		}
153
154		let btn_play = container.find('.player_controls__play');
155		let controls = container.find('.player_controls__wrapper');
156
157		controls.on(this.event_name, (e) => {
158			e.stopPropagation();
159		});
160
161		const controlsShowHide = (type = '') => {
162			let isset_class = controls.hasClass('player_controls__wrapper__hidden');
163			if (type === '') {
164				if (isset_class === true) {
165					controls.removeClass('player_controls__wrapper__hidden');
166				} else {
167					controls.addClass('player_controls__wrapper__hidden');
168				}
169			} else if (type === 'show' && isset_class === true) {
170				controls.removeClass('player_controls__wrapper__hidden');
171			} else if (type === 'hide' && isset_class === false) {
172				controls.addClass('player_controls__wrapper__hidden');
173			}
174		};
175
176		let btn_muted = container.find('.player_controls__muted');
177		let btn_share = container.find('.player__video__share');
178		let preloader = container.find('.player_preloader');
179		let video_preview = container.find('.player__video_preview');
180		let volume_progress = container.find('.player_controls__volume__progress');
181		let volume_bar = container.find('.player_controls__volume');
182		let pg_timeupdate = container.find('.player_controls__timeline');
183		let pg_buffer = container.find('.player_controls__buffer');
184		let pg_timer = container.find('.player_controls__timer');
185		let pg_process = container.find('.player_controls__bar');
186		let fullscreen = container.find('.player_controls__fullscreen');
187
188		let eventTouchContainer = this.event_name;
189		let touchScroll = 0;
190
191		if (ru9111.mobile === true) {
192			eventTouchContainer = 'touchstart touchend touchmove';
193		}
194
195		container.on(eventTouchContainer, (e) => {
196			if (ru9111.mobile === true) {
197				if (e.originalEvent.type === 'touchstart') {
198					touchScroll = 0;
199				}
200				if (e.originalEvent.type === 'touchmove') {
201					touchScroll++;
202				}
203				if (e.originalEvent.type === 'touchend') {
204					if (touchScroll === 0) {
205						if (video.readyState > 2) {
206							if (video.paused || video.ended) {
207								video.play();
208							} else {
209								video.pause();
210							}
211						}
212					}
213				}
214			} else {
215				if (video.readyState > 2) {
216					if (video.paused || video.ended) {
217						video.play();
218					} else {
219						video.pause();
220					}
221				}
222			}
223		});
224
225		preloader.on(this.event_name, (e) => {
226			e.stopPropagation();
227		});
228
229		btn_share.on(this.event_name, (e) => {
230			//console.log(video.titleId);
231			let title_id = 0;
232			if (container.closest('[data-title-id]').length > 0) {
233				title_id = container.closest('[data-title-id]').data('titleId');
234			} else if (container.closest('[data-forum-id]').length > 0) {
235				title_id = container.closest('[data-forum-id]').data('forumId');
236			}
237			if (title_id > 0) {
238				if (ru9111.mobile === false) {
239					statClicks('video_share_desktop');
240					copy2Clip('https://9111.ru/questions/777777777'+title_id+'/', "Готово");
241				} else {
242					statClicks('video_share_mobile');
243					navigatorShare('https://9111.ru/questions/777777777'+title_id+'/');
244				}
245				playerVideoShare(btn_share, 'remove');
246			}
247			e.stopPropagation();
248		});
249
250		btn_play.on(this.event_name, () => {
251			//console.log('btn', video.paused, video.ended, video.readyState);
252			if (video.readyState > 2) {
253				if (video.paused || video.ended) {
254					video.play();
255				} else {
256					video.pause();
257				}
258			} else if (video.ended && video.readyState === 2) {
259				//console.log(1);
260				//video.currentTime = 0;
261				//video.play();
262			}
263		});
264
265		this.video.addEventListener('playing', () => {
266			// videoAdInit(video.id);
267//			console.log("dd");
268//			console.log(this.video.currentTime);
269//			if (this.video.currentTime) {
270//
271//			}
272		}, false);
273
274		this.video.addEventListener('play', () => {
275			playerCurrentVideoStop(video);
276			btn_play.html('<svg><use href="#pause"></use></svg>');
277			//console.log("play", video.id);
278			videoViewingDuration('start');
279			playerVideoShare(btn_share, 'hide');
280		}, false);
281
282		this.video.addEventListener('pause', () => {
283			btn_play.html('<svg><use href="#play"></use></svg>');
284			//console.log("pause", video.id);
285			videoViewingDuration('send', video.id);
286
287			playerPreloader(preloader, 'hide');
288		}, false);
289
290		this.video.addEventListener('ended', () => {
291			video.currentTime = 0;
292			btn_play.html('<svg><use href="#play"></use></svg>');
293			//console.log('Конец');
294			videoViewingDuration('send', video.id);
295			controlsShowHide('show');
296
297			playerVideoShare(btn_share, 'show');
298		});
299
300		container.on('mouseenter mouseover', () => {
301			controlsShowHide('show');
302		});
303
304		container.on('mouseleave', () => {
305			if (video.currentTime > 0 && video.paused === false && video.ended === false && video.readyState > 2) {
306				controlsShowHide('hide');
307			}
308		});
309
310		volume_progress.width((video.volume*100)+'%');
311		this.video.addEventListener('volumechange', () => {
312			if (video.muted === true) {
313				btn_muted.html('<svg><use href="#muted_off"></use></svg>');
314			} else {
315				btn_muted.html('<svg><use href="#muted_on"></use></svg>');
316			}
317
318			let volume_percentage = video.volume*100;
319			if (volume_percentage > 90) {
320				volume_percentage = 100;
321			} else if (volume_percentage <= 15) {
322				volume_percentage = 0;
323			}
324			volume_progress.width(volume_percentage+'%');
325
326			savePlayerConfig('volume', video.volume);
327
328		}, false);
329
330		btn_muted.on(this.event_name, () => {
331			if (video.muted === true) {
332				video.muted = false;
333				video.volume = 1;
334			} else {
335				video.muted = true;
336				video.volume = 0;
337			}
338			savePlayerConfig('muted', video.muted);
339			savePlayerConfig('volume', video.volume);
340		});
341
342		volume_bar.on(this.event_name, (event) => {
343			let target_x = event.offsetX;
344			if (target_x === undefined) {
345				let rect = event.target.getBoundingClientRect();
346				target_x = event.changedTouches[0].pageX - rect.left;
347			}
348			if (target_x && event.currentTarget.offsetWidth) {
349				let volume = (target_x/event.currentTarget.offsetWidth).toFixed(1);
350
351				if (volume < 0.2) {
352					volume = 0;
353				} else if (volume >= 0.9) {
354					volume = 1;
355				}
356				video.volume = volume;
357			}
358		});
359
360		pg_process.on(this.event_name, (event) => {
361			let target_x = event.offsetX;
362			// походу для мобил выпилили offsetX
363			if (target_x === undefined) {
364				let rect = event.target.getBoundingClientRect();
365				target_x = event.changedTouches[0].pageX - rect.left;
366			}
367			if (target_x && video.duration && event.currentTarget.offsetWidth) {
368				video.currentTime = (video.duration/100)*((target_x/event.currentTarget.offsetWidth)*100);
369			}
370		});
371
372		container.on('webkitfullscreenchange fullscreenchange', () => {
373			if (document.webkitFullscreenElement || document.fullscreenElement) {
374				container.attr('data-fullscreen', true);
375				fullscreen.html('<svg><use href="#fullWindowExit"></use></svg>');
376			} else {
377				container.attr('data-fullscreen', false);
378				fullscreen.html('<svg><use href="#fullWindow"></use></svg>');
379			}
380		});
381		fullscreen.on(this.event_name, () => {
382			let elem = document.getElementById(container[0].id);
383			if (document.fullscreenElement) {
384				document.exitFullscreen();
385			} else if (document.webkitFullscreenElement) {
386				document.webkitExitFullscreen();
387			} else if (document.webkitRequestFullscreen) {
388				document.webkitRequestFullscreen();
389			} else if (elem.webkitEnterFullScreen) {
390				elem.webkitEnterFullScreen();
391			} else if (elem.requestFullscreen) {
392				elem.requestFullscreen();
393			} else if (video.webkitEnterFullScreen) {
394				video.webkitEnterFullScreen();
395			} else {
396				elem.requestFullscreen();
397			}
398		});
399
400		this.video.addEventListener('timeupdate', () => {
401			let percentage = Math.round((100 / video.duration) * video.currentTime);
402			pg_timeupdate.width(percentage+"%");
403
404			if (video.buffered && video.buffered.length > 0) {
405				let buffered_percentage = Math.round((video.buffered.end(0) / video.duration) * 100);
406				pg_buffer.width(buffered_percentage+"%");
407			}
408
409			let time = video.duration-video.currentTime;
410			if (time) {
411				let minutes = Math.floor(time / 60);
412				let seconds = Math.floor(time % 60);
413				pg_timer.html(minutes+':'+seconds.toString().padStart(2, "0"));
414			}
415
416			if (video.networkState === video.NETWORK_LOADING) {
417				// The user agent is actively trying to download data.
418				//console.log("loading: ", video.id);
419				playerPreloader(preloader, 'hide');
420			}
421
422			if (video.readyState < video.HAVE_FUTURE_DATA) {
423				// There is not enough data to keep playing from this point
424				//console.log("have data: ", video.id);
425				playerPreloader(preloader, 'show');
426			}
427		}, false);
428
429		// Пользовательский агент пытается получить медиаданные,
430		// но данные неожиданно не получены.
431		this.video.addEventListener('stalled', (e) => {
432			//console.log("stalled: " + video.id, e);
433		}, false);
434
435		// Загрузка медиаданных приостановлена.
436		this.video.addEventListener('suspend', () => {
437			//console.log("suspend: " + video.id);
438		}, false);
439
440		this.video.addEventListener('seeking', () => {
441			//console.log("seeking: " + video.id);
442		}, false);
443
444		this.video.addEventListener('seeked', () => {
445			//console.log("seeked: " + video.id);
446		}, false);
447
448		// Воспроизведение остановлено из-за временного отсутствия данных.
449		this.video.addEventListener('waiting', () => {
450			//console.log("waiting: " + video.id);
451		}, false);
452
453		// Запускается, когда браузер начал загружать ресурс.
454		this.video.addEventListener('loadstart', () => {
455			playerPreloader(preloader, 'show');
456			//console.log("loadstart: " + video.id);
457		}, false);
458
459		// Метаданные загружены.
460		this.video.addEventListener('loadedmetadata', () => {
461			//console.log("loadedmetadata: " + video.id);
462		}, false);
463
464		// Произошла ошибка при получении медиаданных, или тип ресурса
465		// не является поддерживаемым форматом мультимедиа.
466		this.video.addEventListener('error', (e) => {
467			//console.log("error: " + video.id, e);
468		}, false);
469
470		// Браузер может воспроизводить медиафайлы,
471		// но оценивает, что загружено недостаточно данных для воспроизведения мультимедиа до конца
472		// без необходимости остановки для дальнейшей буферизации контента.
473		this.video.addEventListener('canplay', () => {
474			playerPreloader(preloader, 'hide');
475			video_preview.remove();
476		}, false);
477
478		this.video.addEventListener('onplaying', () => {
479			playerPreloader(preloader, 'hide');
480			video_preview.remove();
481		}, false);
482
483		// По оценкам браузера, он может воспроизводить мультимедиа до конца,
484		// не останавливаясь на буферизацию контента.
485		this.video.addEventListener('canplaythrough', () => {
486			//console.log("canplaythrough: " + video.id);
487			playerPreloader(preloader, 'hide');
488		}, false);
489
490		// Загрузка первого кадра мультимедиа завершена.
491		this.video.addEventListener('loadeddata', () => {
492			if (video.autoplay === true && video.readyState > 2) {
493				video.play();
494			}
495			//videoAdInit(video.id);
496			//console.log("loadeddata: " + video.id);
497		}, false);
498
499		this.video.addEventListener("durationchange", (event) => {
500			//console.log("durationchange: ", video.id);
501		}, false);
502
503		//video.src = "";
504		videos[video.id] = this.video;
505		//videos_src[video.id] = video.querySelector('source').getAttribute("src");
506		videos_src[this.video.id] = new VideoSrcModel(this.video.getAttribute("src"), 0);
507		//console.log(video.buffered);
508	}
509};
510
511/**
512 * показываем \ прячем прелоадер
513 * @param {type} preloader
514 * @param {type} type
515 * @returns {undefined}
516 */
517function playerPreloader(preloader, type) {
518	if (type === 'show') {
519		preloader.removeClass("player_preloader__hide");
520		preloader.addClass('player_preloader__show');
521	} else if (type === 'hide') {
522		preloader.removeClass("player_preloader__show");
523		preloader.addClass('player_preloader__hide');
524	}
525}
526
527/**
528 * остановка текущего плеера
529 * @param {Video} video
530 * @returns {undefined}
531 */
532function playerCurrentVideoStop(video) {
533	// идём по массиву и стопарим все видео кроме того что передано, костыль...
534	//console.log("play", video.id);
535	if (currentVideo !== null && currentVideo.id !== video.id && currentVideo.paused !== true) {
536		currentVideo.pause();
537	}
538	currentVideo = video;
539}
540
541/**
542 * клип по кнопки плей
543 */
544$(document).on('click', '.block_item__video_poster', function () {
545	let elem = $(this).closest('.block_item__video_wrapper');
546	let video_url = elem.data('videoUrl');
547	let video_id = Number.parseInt(elem.data('videoId'));
548	let video_type = elem.data('videoType');
549	if (video_url !== "" && video_id > 0 && video_type === 'video') {
550
551		if (playerFirstInit === true && site_vars.player_autoplay === false) {
552			site_vars.player_autoplay = true;
553			playerFirstInit = false;
554			statClicks('video_start');
555			playerAutoplay();
556		}
557
558		videoViewsAnalytics(elem, video_id);
559
560		if (elem.find('.player').length === 0) {
561			const player = new Player(video_id);
562			player.autoplay = site_vars.player_autoplay;
563			elem.append(player.getTemplate(video_url));
564			elem.find('.block_item__video_poster').remove();
565			player.initPlayer();
566			titleVideoHidden(player.getContainer());
567			delete player;
568		}
569	} else if (video_url !== "" && (video_type === 'youtube' || video_type === 'ok_video' || video_type === 'rutube')) {
570		let video_html = '';
571		if (video_type === 'youtube') {
572			video_html = `<iframe id="video_play_status" src="https://www.youtube.com/embed/${video_url}?wmode=opaque&autohide=1&autoplay=1&enablejsapi=1&origin=https://9111.ru" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>`;
573
574		} else if (video_type === 'ok_video') {
575			video_html = `<iframe id="video_play_status" src="https://ok.ru/videoembed/${video_url}" allow="autoplay" allowfullscreen></iframe>`;
576
577		} else if (video_type === 'rutube') {
578			video_html = `<iframe id="video_play_status" src="https://rutube.ru/play/embed/${video_url}" allow="clipboard-write" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>`;
579		}
580
581		if (video_html !== '') {
582			elem.append(video_html);
583			elem.find('.block_item__video_poster').addClass('block_item__video_poster__hide');
584		}
585	}
586});
587
588 function titleVideoHidden(container) {
589	let title_id = 0;
590	if (container.closest('[data-title-id]').length > 0) {
591		title_id = container.closest('[data-title-id]').data('titleId');
592	} else if (container.closest('[data-forum-id]').length > 0) {
593		title_id = container.closest('[data-forum-id]').data('forumId');
594	}
595	if (title_id > 0) {
596		saveHidden(Number.parseInt(title_id));
597		//console.log(title_id);
598	}
599}
600
601/**
602 * сохранение и установка занчения плеера
603 * @param {String} key ключ в объекте
604 * @param {Bool|Int|String} value значение ключа
605 * @returns {undefined}
606 */
607function savePlayerConfig(key, value) {
608	if (key !== undefined && value !== undefined) {
609		player_config[key] = value;
610		localStorage.setItem('player_config', JSON.stringify(player_config));
611	}
612}
613
614/**
615 * загрузка конфига плеера
616 * @returns {void}
617 */
618function loadPlayerConfig() {
619	try {
620		let load_config = localStorage.getItem('player_config');
621		if (load_config) {
622			player_config = JSON.parse(load_config);
623			// так как нельза делать автоплей со звуком
624			//if (site_vars.player_autoplay === true) {
625				//player_config.muted = true;
626			//}
627		}
628	} catch (e) {
629		console.log('Error load config player');
630	}
631}
632
633class VideoSrcModel {
634	src;
635	currentTime;
636	constructor(src, currentTime = 0) {
637		this.src = src;
638		this.currentTime = currentTime;
639	}
640}
641
642let player_autoplay_timer = null;
643
644let player_replay_timer = null;
645
646/**
647 * автовоспроизведение видоса
648 * @param {type} video_id
649 * @returns {undefined}
650 */
651function playerAutoplayTimer(video_id = 0) {
652	//console.log('start');
653	clearTimeout(player_autoplay_timer);
654	player_autoplay_timer = setTimeout((video_id) => {
655		let video_wrap = $('.block_item__video_wrapper[data-video-id="'+video_id+'"]');
656		if (video_wrap) {
657			video_wrap.find('.block_item__video_poster').click();
658			//console.log('впервые?', video_id);
659		}
660	}, 500, video_id);
661}
662
663function playerReplayTimer(video_id = 0) {
664	clearTimeout(player_replay_timer);
665	player_replay_timer = setTimeout((video_id) => {
666		//console.log('опять?', video_id);
667		let video = videos.at(video_id);
668		if (video !== undefined && video.paused === true && video.ended === false) {
669			try {
670				video.play();
671				videoViewingDuration('start');
672				//console.log('включаем', video_id);
673			} catch (e) {
674				console.log("Err 1", e, video.id);
675			}
676		}
677		delete video;
678	}, 500, video_id);
679}
680
681function playerStopVideo(video_id = 0) {
682	if (video_id > 0) {
683		let video = videos.at(lastVideoIdInView);
684		if (video !== undefined && video.paused === false) {
685			video.pause();
686			videos_src[video.id] = new VideoSrcModel(
687				video.src,
688				video.currentTime
689			);
690			//video.src = "";
691			//video.querySelector('source').setAttribute('src', "");
692			//console.log('отключаем', video.id);
693			videoViewingDuration('send', video.id);
694		}
695		delete video;
696	}
697}
698
699/**
700 * последний айди, используется в playerAutoplay и playerAutostop
701 * @type Number|video_id
702 */
703let lastVideoIdInView = 0;
704
705/**
706 * последний объект в области видимости, используется в playerAutoplay и playerAutostop
707 * @type entry
708 */
709let last_entry = null;
710
711/**
712 * область пересечения
713 * @type Number
714 */
715let threshold = 0.8;
716if ($(window).height() > ($(window).width()/2) && ru9111.mobile === true) {
717	threshold = 0.9;
718} else if ($(window).height() > $(window).width() && ru9111.mobile === true) {
719	threshold = 0.6;
720} else if ($(window).height() > 600) {
721	threshold = 0.9;
722} else if ($(window).height() >= 740) {
723	threshold = 1.0;
724}
725
726/**
727 * автововоспроизведение плеера, не запускать вместе с playerAutostop!!!!!
728 * @returns {undefined}
729 */
730let playerAutoplay = () => {
731	const observer = new IntersectionObserver((entries, observer) => {
732		entries.forEach(entry => {
733			//console.log(entry.target);
734			//console.log(lastVideoIdInView);
735			if (last_entry !== null && last_entry.isIntersecting === false && lastVideoIdInView > 0) {
736				playerStopVideo(lastVideoIdInView);
737				lastVideoIdInView = 0;
738				last_entry = null;
739			}
740
741			if (entry.isIntersecting) {
742				last_entry = entry;
743				lastVideoIdInView = Number.parseInt($(entry.target).closest('.block_item__video_wrapper[data-vi
743deo-type="video"]').data("videoId"));
744			} else {
745				clearTimeout(player_autoplay_timer);
746				clearTimeout(player_replay_timer);
747				if (currentVideo !== null) {
748					playerStopVideo(currentVideo.id);
749				}
750			}
751
752//			if (entry.isIntersecting) {
753//				last_entry = entry;
754//				let video_id = Number.parseInt($(entry.target).closest('.block_item__video_wrapper[data-video-type="video"]').data("videoId"));
755//				console.log("in_view", video_id);
756//				console.log(site_vars.player_autoplay);
757//				if (lastVideoIdInView > 0) {
758//					if (lastVideoIdInView !== video_id) {
759//						playerStopVideo(lastVideoIdInView);
760//						lastVideoIdInView = 0;
761//
762//						if (videos.at(video_id) === undefined) {
763//							//console.log('опять?', video_id);
764//							playerAutoplayTimer(video_id);
765//						} else {
766//							playerReplayTimer(video_id);
767//						}
768//						lastVideoIdInView = video_id;
769//					} else {
770//						if (videos.at(lastVideoIdInView) !== undefined) {
771//							console.log('опять?', video_id);
772//							playerReplayTimer(video_id);
773//						}
774//					}
775//				} else if (video_id > 0) {
776//					if (videos.at(video_id) === undefined) {
777//						playerAutoplayTimer(video_id);
778//					} else {
779//						playerReplayTimer(video_id);
780//					}
781//					lastVideoIdInView = video_id;
782//				}
783//				//console.log(video_id);
784//			} else {
785//				clearTimeout(player_autoplay_timer);
786//				clearTimeout(player_replay_timer);
787//				if (lastVideoIdInView > 0) {
788//					playerStopVideo(lastVideoIdInView);
789//					lastVideoIdInView = 0;
790//				}
791//			}
792		});
793	}, {
794		//root: document.querySelector('.block_item__video_wrapper'),
795		threshold: threshold,
796		//trackVisibility: true,
797		//delay: 1000
798	});
799	document.querySelectorAll('.block_item__video_wrapper[data-video-type="video"]').forEach(video => observer.observe(video));
800};
801
802/**
803 * автоостановка плеера, НЕ ЗАПУСКАТЬ ВМЕСТЕ С playerAutoplay
804 * @returns {undefined}
805 */
806let playerAutostop = () => {
807	const observer = new IntersectionObserver((entries, observer) => {
808		entries.forEach(entry => {
809			if (entry.isIntersecting) {
810				last_entry = entry;
811				lastVideoIdInView = Number.parseInt($(entry.target).closest('.block_item__video_wrapper[data-video-type="video"]').data("videoId"));
812			} else {
813				if (lastVideoIdInView > 0) {
814					playerStopVideo(lastVideoIdInView);
815					lastVideoIdInView = 0;
816				}
817			}
818		});
819	}, {
820		//root: document.querySelector('.block_item__video_wrapper'),
821		threshold: threshold,
822		//trackVisibility: true,
823		//delay: 1000
824	});
825	document.querySelectorAll('.block_item__video_wrapper[data-video-type="video"]').forEach(video => observer.observe(video));
826};
827
828/**
829 * запускаем мутацию и обсервер
830 * @type null|Object
831 */
832const targetNode = document.querySelector('#main_page_content') || document.querySelector('#see_also_titles');
833if (targetNode) {
834	//console.log(111);
835	const MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
836
837	let observer = new MutationObserver((entries, observer) => {
838		// console.log(entries);
839		// playerAutoplay();
840		entries.forEach(entry => {
841			//console.log(entry.target.classList);
842			if (entry.target.classList.contains('ArticleContent')) {
843				if (site_vars.player_autoplay === true) {
844					//playerAutoplay();
845				} else {
846					//playerAutostop();
847				}
848				return;
849			}
850		});
851	});
852	const observer_config = {
853		childList: true,
854		subtree: true,
855		target: '.block_item__video_wrapper[data-video-type="video"]',
856		//attributes: true
857	};
858	observer.observe(targetNode, observer_config);
859}
860
861$(document).ready(function() {
862	loadPlayerConfig();
863	if (site_vars.player_autoplay === true) {
864		playerAutoplay();
865	} else {
866		playerAutostop();
867	}
868});
869
870/**
871 * флаг работы скрипта на текущий момент
872 * @type Boolean
873 */
874let video_view_anal_insert_status = false;
875/**
876 * просмотры видео
877 * @param {type} elem
878 * @param {type} video_id
879 * @param {Number} shorts 1 = шорты, 0 = обычный видос
880 * @returns {undefined}
881 */
882function videoViewsAnalytics(elem, video_id = 0, shorts = 0) {
883	if (elem && video_id > 0 && video_view_anal_insert_status === false) {
884		let title_id = 0;
885		if ($(elem).closest('[data-title-id]').length > 0) {
886			title_id = $(elem).closest('[data-title-id]').data('titleId');
887		} else if ($(elem).closest('[data-forum-id]').length > 0) {
888			title_id = $(elem).closest('[data-forum-id]').data('forumId');
889		}
890		if (video_id > 0) {
891			video_view_anal_insert_status = true;
892			$.ajax({
893				cache: false,
894				type: 'POST',
895				url: '/api/analytics/video_view_anal.php',
896				dataType: 'json',
897				data: {
898					video_id: video_id,
899					title_id: title_id,
900					referrer: document.referrer,
901					shorts: shorts,
902					url: window.location.href
903				},
904				success: function (data) {
905					//console.log(data);
906					video_view_anal_insert_status = false;
907				}
908			});
909		}
910	}
911}
912/**
913 * длительность просмотра видоса
914 * @type Number
915 */
916let video_viewing_duration = 0;
917/**
918 * таймер
919 * @type Function
920 */
921let watch_video_timer = null;
922/**
923 * тправялем данные по длительности просмотра
924 * @param {String} type
925 * @param {Number} video_id
926 * @returns {undefined}
927 */
928function videoViewingDuration(type = "", video_id = 0) {
929
930	let timer_stop = () => {
931		if (watch_video_timer !== null) {
932			clearTimeout(watch_video_timer);
933			watch_video_timer = null;
934			video_viewing_duration = 0;
935		}
936	};
937
938	if (type === "start") {
939		timer_stop();
940		watch_video_timer = setInterval(() => {
941			video_viewing_duration++;
942			//console.log("Уже смотрят: ", video_viewing_duration, " сек.");
943		}, 1000);
944
945	} else if (type === "stop") {
946		timer_stop();
947
948	} else if (type === 'send') {
949		if (video_id > 0 && video_viewing_duration > 0) {
950			//console.log("Отправляем: ", video_id, video_viewing_duration, " сек.");
951			let obj_to_send = {
952				query: 'send',
953				mode: 'video_view_duration',
954				video_id: Number.parseInt(video_id),
955				duration: video_viewing_duration
956			};
957			if (ru9111.id > 0) {
958				obj_to_send.user_id = ru9111.id;
959			}
960			wsSend(obj_to_send);
961			//console.log(type, video_id, video_viewing_duration);
962			timer_stop();
963		}
964	}
965}
966
967function playerVideoShare(elem, type) {
968	let elemRoot = elem.closest('.player__video__share__wrap');
969	if (elemRoot) {
970		if (type === 'show') {
971			elemRoot.addClass('player__video__share__show');
972			elemRoot.removeClass('player__video__share__hide');
973		} else if (type === 'hide') {
974			elemRoot.addClass('player__video__share__hide');
975			elemRoot.removeClass('player__video__share__show');
976		} else if (type === 'remove') {
977			elemRoot.remove();
978		}
979	}
980}
981
982/**
983 * инициализация рекламы
984 * @param {Number} video_id
985 * @returns {undefined}
986 */
987function videoAdInit(video_id) {
988
989	//console.log(ya.videoAd);
990	if (typeof ya.videoAd.play === "function") {
991		let show_video_advert = undefined; //localStorage.getItem('show_video_advert');
992		if (show_video_advert === undefined || show_video_advert < getTimeStamp()) {
993
994			localStorage.setItem('show_video_advert', (getTimeStamp()+3000));
995
996			$('#player'+video_id).after(`<div id="player_adfox"><video id="video_adfox" poster="" src=""></video></div>`);
997			$('#player_adfox').fadeIn(50);
998
999			let container_id = 'player_adfox';
1000			let container = document.getElementById(container_id);
1001			let video_elem = container.getElementsByTagName('video')[0];
1002			let curVideo = $('#player'+video_id).find('video')[0];
1003			// document.getElementById(container.getElementsByTagName('video')[0].getAttribute('id'));
1004			if (container && video_elem) {
1005				$('#player'+video_id).fadeOut(50, function(){
1006					$('#player_adfox').fadeIn(50);
1007				});
1008				let adFoxParameters = {
1009					adFoxParameters: {
1010						ownerId: "282719",
1011						// ID родительского контейнера тега <video></video>
1012						containerId: container_id,
1013						// Параметры ADFOX
1014						params: {
1015							pp: "g",
1016							ps: "ddlt",
1017							p2: "hnob"
1018						}
1019					}
1020				};
1021				//console.log(ya);
1022				if (curVideo !== null && curVideo.paused !== true) {
1023					curVideo.pause();
1024				}
1025				ya.videoAd.play({
1026					// Видеоэлемент
1027					videoSlot: video_elem,
1028					// Элемент для показа контролов. Элемент не должен быть статически позиционирован.
1029					// Можно использовать родительский элемент видео.
1030					slot: container,
1031					// Конфиг загрузки и конфиг проигрывания рекламы.
1032					//config: AdConfig & PlaybackParameters & adFoxParameters,
1033					config: adFoxParameters
1034				}).then(
1035					(resolve) => {
1036						$('#player_adfox').fadeOut(50, function(){
1037							$('#player'+video_id).fadeIn(50);
1038							$('#player_adfox').remove();
1039						});
1040						if (curVideo !== null && curVideo.paused) {
1041							curVideo.play();
1042						}
1043						//console.log("dd");
1044						//playerRemove('player');
1045						//console.log("1");
1046					},//console.log(resolve),
1047					(reject) => {
1048						$('#player_adfox').fadeOut(50, function(){
1049							$('#player'+video_id).fadeIn(50);
1050							$('#player_adfox').remove();
1051						});
1052						if (curVideo !== null && curVideo.paused) {
1053							curVideo.play();
1054						}
1055						//console.log("ff");
1056						//console.log("2");
1057					}
1058				);
1059			}
1060		}
1061	}
1062}
1063
1064/**
1065 * таймер отслеживания текущего события прокрутки страницы
1066 * @type Timeout
1067 */
1068let scrollEventTimer = null;
1069
1070/**
1071 * крутим сейчас или нет
1072 * @type Boolean
1073 */
1074let playerEventScroll = false;
1075
1076/**
1077 * слашатель скроллера
1078 */
1079window.addEventListener('scroll', () => {
1080	if (scrollEventTimer !== null) {
1081		clearTimeout(scrollEventTimer);
1082		//console.log('крутим');
1083		playerEventScroll = true;
1084    }
1085	scrollEventTimer = setTimeout(() => {
1086		//console.log('перестали');
1087		//console.log(lastVideoIdInView);
1088		playerEventScroll = false;
1089		if (currentVideo !== null) {
1090			if (currentVideo.id !== lastVideoIdInView) {
1091				if (videos.at(lastVideoIdInView) === undefined) {
1092					playerAutoplayTimer(lastVideoIdInView);
1093				} else {
1094					playerReplayTimer(lastVideoIdInView);
1095				}
1096			}
1097		}
1098	}, 250);
1099}, false);

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.