@splidejs/splide-extension-video
Version:
The Splide extension for embedding videos.
230 lines (192 loc) • 4.84 kB
text/typescript
import { AnyFunction, EventInterface, State } from '@splidejs/splide';
import { error } from '@splidejs/splide/src/js/utils';
import {
ERROR,
IDLE,
INITIALIZED,
INITIALIZING,
LOADING,
NOT_INITIALIZED,
PENDING_PLAY,
PLAY_REQUEST_ABORTED,
PLAYING,
} from '../constants/states';
import { VideoPlayerInterface } from '../types/general';
import { VideoOptions } from '../types/options';
/**
* The abstract class for implementing a video player.
*
* @since 0.5.0
*/
export abstract class AbstractVideoPlayer<T> implements VideoPlayerInterface {
/**
* The target element where the player is mounted.
*/
protected readonly target: HTMLElement;
/**
* The video ID or the URL itself.
*/
protected videoId: string;
/**
* Video options.
*/
protected readonly options: VideoOptions;
/**
* The state object.
*/
protected state = State( NOT_INITIALIZED );
/**
* The EventBus object.
*/
protected event = EventInterface();
/**
* The player instance.
*/
protected player: T;
/**
* The VideoPlayer constructor.
*
* @param target - A target element where the player is mounted.
* @param videoId - A video ID or an URL itself.
* @param options - Optional. Options.
*/
protected constructor( target: HTMLElement, videoId: string, options?: VideoOptions ) {
this.target = target;
this.videoId = videoId;
this.options = options || {};
this.onPlay = this.onPlay.bind( this );
this.onPause = this.onPause.bind( this );
this.onEnded = this.onEnded.bind( this );
this.onPlayerReady = this.onPlayerReady.bind( this );
this.onError = this.onError.bind( this );
}
/**
* Creates a player.
*
* @param videoId - A video ID.
*
* @return A VideoPlayerInterface object.
*/
protected abstract createPlayer( videoId: string ): T;
/**
* Starts the video.
*/
protected abstract playVideo(): void;
/**
* Pauses the video.
*/
protected abstract pauseVideo(): void;
/**
* Attaches a handler to a specified event or events.
*
* @param events - An event or events to attach a handler to.
* @param callback - A callback function.
*/
on( events: string | string[], callback: AnyFunction ): void {
this.event.on( events, callback );
}
/**
* Requests to play the video.
*/
play(): void {
const { state } = this;
if ( state.is( ERROR ) ) {
error( 'Can not play this video.' );
return;
}
this.event.emit( 'play' );
if ( state.is( INITIALIZING ) ) {
return state.set( PENDING_PLAY );
}
if ( state.is( INITIALIZED ) ) {
this.player = this.createPlayer( this.videoId );
return state.set( PENDING_PLAY );
}
if ( state.is( [ PENDING_PLAY, PLAYING ] ) ) {
return;
}
if ( state.is( IDLE ) ) {
state.set( LOADING );
this.playVideo();
}
}
/**
* Requests to pause the video.
*/
pause(): void {
const { state } = this;
if ( state.is( ERROR ) ) {
return;
}
this.event.emit( 'pause' );
if ( state.is( PENDING_PLAY ) ) {
return state.set( INITIALIZING );
}
if ( state.is( LOADING ) ) {
return state.set( PLAY_REQUEST_ABORTED );
}
if ( state.is( PLAYING ) ) {
this.pauseVideo();
this.state.set( IDLE );
}
}
/**
* Checks if the video is paused or not.
*
* @return `true` if the video is paused.
*/
isPaused(): boolean {
return ! this.state.is( PLAYING );
}
/**
* Destroys the instance.
*/
destroy(): void {
this.event.destroy();
}
/**
* Called when the player is created.
*/
protected onPlayerReady(): void {
const { state } = this;
const isPending = state.is( PENDING_PLAY );
state.set( IDLE );
if ( isPending ) {
this.play();
}
}
/**
* Called when the video starts.
*/
protected onPlay(): void {
const { state } = this;
const aborted = state.is( PLAY_REQUEST_ABORTED );
state.set( PLAYING );
if ( aborted ) {
this.pause();
} else {
this.event.emit( 'played' );
}
}
/**
* Called when the video is paused.
*/
protected onPause(): void {
this.state.set( IDLE );
this.event.emit( 'paused' );
}
/**
* Called when the video is ended.
*/
protected onEnded(): void {
this.state.set( IDLE );
this.event.emit( 'ended' );
}
/**
* Called when an error occurs.
*/
protected onError(): void {
this.state.set( ERROR );
this.event.emit( 'error' );
}
}