403Webshell
Server IP : 80.87.202.40  /  Your IP : 216.73.216.169
Web Server : Apache
System : Linux rospirotorg.ru 5.14.0-539.el9.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Dec 5 22:26:13 UTC 2024 x86_64
User : bitrix ( 600)
PHP Version : 8.2.27
Disable Function : NONE
MySQL : OFF |  cURL : ON |  WGET : ON |  Perl : ON |  Python : OFF |  Sudo : ON |  Pkexec : ON
Directory :  /home/bitrix/ext_www/cvetdv.ru/bitrix/js/im/v2/component/elements/audioplayer/src/

Upload File :
current_dir [ Writeable] document_root [ Writeable]

 

Command :


[ Back ]     

Current File : /home/bitrix/ext_www/cvetdv.ru/bitrix/js/im/v2/component/elements/audioplayer/src/audioplayer.js
import 'ui.fonts.opensans';
import 'main.polyfill.intersectionobserver';
import { BaseEvent, EventEmitter } from 'main.core.events';

import { LocalStorageKey, AudioPlaybackRate, AudioPlaybackState as State, EventType } from 'im.v2.const';
import { LocalStorageManager } from 'im.v2.lib.local-storage';
import { Utils } from 'im.v2.lib.utils';
import { MessageAvatar, AvatarSize } from 'im.v2.component.elements.avatar';

import './audioplayer.css';

import type { JsonObject } from 'main.core';

const ID_KEY = 'im:audioplayer:id';

// @vue/component
export const AudioPlayer = {
	name: 'AudioPlayer',
	components: { MessageAvatar },
	props: {
		id: {
			type: Number,
			default: 0,
		},
		src: {
			type: String,
			default: '',
		},
		file: {
			type: Object,
			required: true,
		},
		authorId: {
			type: Number,
			required: true,
		},
		messageId: {
			type: [String, Number],
			required: true,
		},
		timelineType: {
			type: Number,
			required: true,
		},
		withContextMenu: {
			type: Boolean,
			default: true,
		},
		withAvatar: {
			type: Boolean,
			default: true,
		},
		withPlaybackRateControl: {
			type: Boolean,
			default: false,
		},
	},
	data(): JsonObject {
		return {
			preload: 'none',
			loaded: false,
			loading: false,
			state: State.none,
			progress: 0,
			progressInPixel: 0,
			seek: 0,
			timeCurrent: 0,
			timeTotal: 0,
			showContextButton: false,
			currentRate: AudioPlaybackRate['1'],
		};
	},
	computed:
	{
		State: () => State,
		seekPosition(): string
		{
			if (!this.loaded && !this.seek)
			{
				return 'display: none';
			}

			return `left: ${this.seek}px;`;
		},
		isPlaying(): boolean
		{
			return this.state === State.play;
		},
		labelTime(): string
		{
			if (!this.loaded && !this.timeTotal)
			{
				return '--:--';
			}

			let time = 0;
			if (this.isPlaying)
			{
				time = this.timeTotal - this.timeCurrent;
			}
			else
			{
				time = this.timeTotal;
			}

			return Utils.date.formatMediaDurationTime(time);
		},
		AvatarSize: () => AvatarSize,
		fileSize(): string
		{
			return Utils.file.formatFileSize(this.file.size);
		},
		progressPosition(): { width: string }
		{
			if (!this.loaded || this.state === State.none)
			{
				return { width: '100%' };
			}

			return { width: `${this.progressInPixel}px` };
		},
		activeTimelineStyles(): { [className: string]: string }
		{
			const TIMELINE_VERTICAL_SHIFT = 44;
			const ACTIVE_TIMELINE_VERTICAL_SHIFT = 19;

			const shift = this.timelineType * TIMELINE_VERTICAL_SHIFT + ACTIVE_TIMELINE_VERTICAL_SHIFT;

			return {
				...this.progressPosition,
				'background-position-y': `-${shift}px`,
			};
		},
		timelineStyles(): { [className: string]: string }
		{
			const TIMELINE_VERTICAL_SHIFT = 44;

			const shift = this.timelineType * TIMELINE_VERTICAL_SHIFT;

			return {
				'background-position-y': `-${shift}px`,
			};
		},
		getAudioPlayerIds(): Array
		{
			return this.$Bitrix.Data.get(ID_KEY, []);
		},
		currentRateLabel(): string
		{
			return this.isPlaying ? `${this.currentRate}x` : '';
		},
		metaInfo(): string
		{
			return `${this.fileSize}, ${this.labelTime}`;
		},
	},
	watch:
	{
		id(value: number)
		{
			this.registerPlayer(value);
		},
		progress(value: number)
		{
			if (value > 70)
			{
				this.preloadNext();
			}
		},
	},
	created()
	{
		this.localStorageInst = LocalStorageManager.getInstance();
		this.currentRate = this.getRateFromLS();

		this.preloadRequestSent = false;
		this.registeredId = 0;

		this.registerPlayer(this.id);
		EventEmitter.subscribe(EventType.audioPlayer.play, this.onPlay);
		EventEmitter.subscribe(EventType.audioPlayer.stop, this.onStop);
		EventEmitter.subscribe(EventType.audioPlayer.pause, this.onPause);
		EventEmitter.subscribe(EventType.audioPlayer.preload, this.onPreload);
	},
	mounted()
	{
		this.getObserver().observe(this.$refs.body);
	},
	beforeUnmount()
	{
		this.unregisterPlayer();

		EventEmitter.unsubscribe(EventType.audioPlayer.play, this.onPlay);
		EventEmitter.unsubscribe(EventType.audioPlayer.stop, this.onStop);
		EventEmitter.unsubscribe(EventType.audioPlayer.pause, this.onPause);
		EventEmitter.unsubscribe(EventType.audioPlayer.preload, this.onPreload);

		this.getObserver().unobserve(this.$refs.body);
	},
	methods:
	{
		loadFile(play: boolean = false)
		{
			if (this.loaded || (this.loading && !play))
			{
				return;
			}

			this.preload = 'auto';

			if (!play)
			{
				return;
			}

			this.loading = true;

			if (this.source())
			{
				void this.source().play();
			}
		},
		clickToButton()
		{
			if (!this.src)
			{
				return;
			}

			if (this.isPlaying)
			{
				this.pause();
			}
			else
			{
				this.play();
			}
		},
		play()
		{
			this.updateRate(this.getRateFromLS());

			if (!this.loaded)
			{
				this.loadFile(true);

				return;
			}

			void this.source().play();
		},
		pause()
		{
			this.source().pause();
		},
		stop()
		{
			this.state = State.stop;
			this.source().pause();
		},
		setPosition()
		{
			if (!this.loaded)
			{
				this.loadFile(true);

				return;
			}

			const pixelPerPercent = this.$refs.track.offsetWidth / 100;

			this.setProgress(this.seek / pixelPerPercent, this.seek);

			if (this.state !== State.play)
			{
				this.state = State.pause;
			}

			this.play();
			this.source().currentTime = this.timeTotal / 100 * this.progress;
		},
		getRateFromLS(): $Values<typeof AudioPlaybackRate>
		{
			return this.localStorageInst.get(LocalStorageKey.audioPlaybackRate) || AudioPlaybackRate['1'];
		},
		setRateInLS(newRate: $Values<typeof AudioPlaybackRate>)
		{
			this.localStorageInst.set(LocalStorageKey.audioPlaybackRate, newRate);
		},
		getNextPlaybackRate(currentRate: $Values<typeof AudioPlaybackRate>): $Values<typeof AudioPlaybackRate>
		{
			const rates = Object.values(AudioPlaybackRate).sort();
			const currentIndex = rates.indexOf(currentRate);
			const nextIndex = (currentIndex + 1) % rates.length;

			return rates[nextIndex];
		},
		changeRate()
		{
			if ([State.pause, State.none].includes(this.state))
			{
				return;
			}

			const commonCurrentRate = this.getRateFromLS();
			const newRate = this.getNextPlaybackRate(commonCurrentRate);

			this.setRateInLS(newRate);
			this.updateRate(newRate);
		},
		updateRate(newRate: $Values<typeof AudioPlaybackRate>)
		{
			this.currentRate = newRate;
			this.source().playbackRate = newRate;
		},
		seeking(event: MouseEvent): boolean
		{
			if (!this.loaded)
			{
				return;
			}

			this.seek = event.offsetX > 0 ? event.offsetX : 0;
		},
		setProgress(percent: number, pixel: number = -1)
		{
			this.progress = percent;
			this.progressInPixel = pixel > 0 ? pixel : Math.round(this.$refs.track.offsetWidth / 100 * percent);
		},
		registerPlayer(id: number): boolean
		{
			if (id <= 0)
			{
				return;
			}

			this.unregisterPlayer();
			const audioIdArray = [...new Set([...this.getAudioPlayerIds, id])];
			this.$Bitrix.Data.set(ID_KEY, audioIdArray.sort((a, b) => a - b));

			this.registeredId = id;
		},
		unregisterPlayer(): boolean
		{
			if (!this.registeredId)
			{
				return;
			}

			this.$Bitrix.Data.get(ID_KEY, this.getAudioPlayerIds.filter((id) => id !== this.registeredId));

			this.registeredId = 0;
		},
		playNext(): boolean
		{
			if (!this.registeredId)
			{
				return;
			}

			const nextId = this.getAudioPlayerIds.filter((id) => id > this.registeredId).slice(0, 1)[0];
			if (nextId)
			{
				EventEmitter.emit(EventType.audioPlayer.play, { id: nextId, start: true });
			}
		},
		preloadNext(): boolean
		{
			if (this.preloadRequestSent || !this.registeredId)
			{
				return;
			}

			this.preloadRequestSent = true;

			const nextId = this.getAudioPlayerIds.filter((id) => id > this.registeredId).slice(0, 1)[0];
			if (nextId)
			{
				EventEmitter.emit(EventType.audioPlayer.preload, { id: nextId });
			}
		},
		onPlay(event: BaseEvent)
		{
			const data = event.getData();

			if (data.id !== this.id)
			{
				return;
			}

			if (data.start)
			{
				this.stop();
			}

			this.play();
		},
		onStop(event: BaseEvent)
		{
			const data = event.getData();

			if (data.initiator === this.id)
			{
				return;
			}

			this.stop();
		},
		onPause(event: BaseEvent)
		{
			const data = event.getData();

			if (data.initiator === this.id)
			{
				return;
			}

			this.pause();
		},
		onPreload(event: BaseEvent)
		{
			const data = event.getData();

			if (data.id !== this.id)
			{
				return;
			}

			this.loadFile();
		},
		source(): HTMLAudioElement
		{
			return this.$refs.source;
		},
		audioEventRouter(eventName: string, event: BaseEvent)
		{
			// eslint-disable-next-line default-case
			switch (eventName)
			{
				case 'durationchange':
				case 'loadeddata':
				case 'loadedmetadata':
					if (!this.source())
					{
						return;
					}
					this.timeTotal = this.source().duration;

					break;
				case 'abort':
				case 'error':
					console.error('BxAudioPlayer: load failed', this.id, event);

					this.loading = false;
					this.state = State.none;
					this.timeTotal = 0;
					this.preload = 'none';

					break;
				case 'canplaythrough':
					this.loading = false;
					this.loaded = true;

					break;
				case 'timeupdate':
					if (!this.source())
					{
						return;
					}

					this.timeCurrent = this.source().currentTime;

					this.setProgress(Math.round(100 / this.timeTotal * this.timeCurrent));

					if (this.isPlaying && this.timeCurrent >= this.timeTotal)
					{
						this.playNext();
					}

					break;
				case 'pause':
					if (this.state !== State.stop)
					{
						this.state = State.pause;
					}

					break;
				case 'play':
					this.state = State.play;

					if (this.state === State.stop)
					{
						this.progress = 0;
						this.timeCurrent = 0;
					}

					if (this.id > 0)
					{
						EventEmitter.emit(EventType.audioPlayer.pause, { initiator: this.id });
					}

					break;
				// No default
			}
		},
		getObserver(): IntersectionObserver
		{
			if (this.observer)
			{
				return this.observer;
			}

			this.observer = new IntersectionObserver((entries) => {
				entries.forEach((entry) => {
					if (entry.isIntersecting && this.preload === 'none')
					{
						this.preload = 'metadata';
						this.observer.unobserve(entry.target);
					}
				});
			}, {
				threshold: [0, 1],
			});

			return this.observer;
		},
	},
	template: `
		<div 
			class="bx-im-audio-player__container bx-im-audio-player__scope" 
			ref="body"
			@mouseover="showContextButton = true"
			@mouseleave="showContextButton = false"
		>
			<div class="bx-im-audio-player__control-container">
				<button
					class="bx-im-audio-player__control-button"
					:class="{
						'bx-im-audio-player__control-loader': loading,
						'bx-im-audio-player__control-play': !loading && !this.isPlaying,
						'bx-im-audio-player__control-pause': !loading && this.isPlaying,
					}"
					@click="clickToButton"
				></button>
				<div v-if="withAvatar" class="bx-im-audio-player__author-avatar-container">
					<MessageAvatar
						:messageId="messageId"
						:authorId="authorId"
						:size="AvatarSize.XS" 
					/>
				</div>
			</div>
			<div class="bx-im-audio-player__timeline-container">
				<div class="bx-im-audio-player__track-container" @click="setPosition" ref="track">
					<div class="bx-im-audio-player__track-mask" :style="timelineStyles"></div>
					<div class="bx-im-audio-player__track-mask --active" :style="activeTimelineStyles"></div>
					<div class="bx-im-audio-player__track-seek" :style="seekPosition"></div>
					<div class="bx-im-audio-player__track-event" @mousemove="seeking"></div>
				</div>
				<div class="bx-im-audio-player__timer-container --ellipsis">
					{{metaInfo}}
				</div>
			</div>
			<div
				v-if="!withPlaybackRateControl"
				class="bx-im-audio-player__rate-button-container"
			>
				<button
					:class="{'--active': isPlaying}"
					@click="changeRate"
				>
					<span :class="{'bx-im-audio-player__rate-icon': !isPlaying}">
						{{currentRateLabel}}
					</span>
				</button>
			</div>
			<button
				v-if="showContextButton && withContextMenu"
				class="bx-im-messenger__context-menu-icon bx-im-audio-player__context-menu-button"
				@click="$emit('contextMenuClick', $event)"
			></button>
			<audio 
				v-if="src" 
				:src="src" 
				class="bx-im-audio-player__audio-source" 
				ref="source" 
				:preload="preload"
				@abort="audioEventRouter('abort', $event)"
				@error="audioEventRouter('error', $event)"
				@suspend="audioEventRouter('suspend', $event)"
				@canplay="audioEventRouter('canplay', $event)"
				@canplaythrough="audioEventRouter('canplaythrough', $event)"
				@durationchange="audioEventRouter('durationchange', $event)"
				@loadeddata="audioEventRouter('loadeddata', $event)"
				@loadedmetadata="audioEventRouter('loadedmetadata', $event)"
				@timeupdate="audioEventRouter('timeupdate', $event)"
				@play="audioEventRouter('play', $event)"
				@playing="audioEventRouter('playing', $event)"
				@pause="audioEventRouter('pause', $event)"
			></audio>
		</div>
	`,
};

Youez - 2016 - github.com/yon3zu
LinuXploit