192 lines
6.3 KiB
JavaScript
192 lines
6.3 KiB
JavaScript
// ==========================================================================
|
|
// Plyr Captions
|
|
// ==========================================================================
|
|
|
|
import support from './support';
|
|
import utils from './utils';
|
|
import controls from './controls';
|
|
import storage from './storage';
|
|
|
|
const captions = {
|
|
// Setup captions
|
|
setup() {
|
|
// Requires UI support
|
|
if (!this.supported.ui) {
|
|
return;
|
|
}
|
|
|
|
// Set default language if not set
|
|
if (!utils.is.empty(storage.get.call(this).language)) {
|
|
this.captions.language = storage.get.call(this).language;
|
|
} else if (utils.is.empty(this.captions.language)) {
|
|
this.captions.language = this.config.captions.language.toLowerCase();
|
|
}
|
|
|
|
// Set captions enabled state if not set
|
|
if (!utils.is.boolean(this.captions.enabled)) {
|
|
if (!utils.is.empty(storage.get.call(this).language)) {
|
|
this.captions.enabled = storage.get.call(this).captions;
|
|
} else {
|
|
this.captions.enabled = this.config.captions.active;
|
|
}
|
|
}
|
|
|
|
// Only Vimeo and HTML5 video supported at this point
|
|
if (!this.isVideo || this.isYouTube || (this.isHTML5 && !support.textTracks)) {
|
|
// Clear menu and hide
|
|
if (this.config.controls.includes('settings') && this.config.settings.includes('captions')) {
|
|
controls.setCaptionsMenu.call(this);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Inject the container
|
|
if (!utils.is.htmlElement(this.elements.captions)) {
|
|
this.elements.captions = utils.createElement('div', utils.getAttributesFromSelector(this.config.selectors.captions));
|
|
|
|
utils.insertAfter(this.elements.captions, this.elements.wrapper);
|
|
}
|
|
|
|
// Set the class hook
|
|
utils.toggleClass(this.elements.container, this.config.classNames.captions.enabled, !utils.is.empty(captions.getTracks.call(this)));
|
|
|
|
// If no caption file exists, hide container for caption text
|
|
if (utils.is.empty(captions.getTracks.call(this))) {
|
|
return;
|
|
}
|
|
|
|
// Set language
|
|
captions.setLanguage.call(this);
|
|
|
|
// Enable UI
|
|
captions.show.call(this);
|
|
|
|
// Set available languages in list
|
|
if (this.config.controls.includes('settings') && this.config.settings.includes('captions')) {
|
|
controls.setCaptionsMenu.call(this);
|
|
}
|
|
},
|
|
|
|
// Set the captions language
|
|
setLanguage() {
|
|
// Setup HTML5 track rendering
|
|
if (this.isHTML5 && this.isVideo) {
|
|
captions.getTracks.call(this).forEach(track => {
|
|
// Remove previous bindings
|
|
utils.on(track, 'cuechange', event => captions.setCue.call(this, event));
|
|
|
|
// Turn off native caption rendering to avoid double captions
|
|
// eslint-disable-next-line
|
|
track.mode = 'hidden';
|
|
});
|
|
|
|
// Get current track
|
|
const currentTrack = captions.getCurrentTrack.call(this);
|
|
|
|
// Check if suported kind
|
|
if (utils.is.track(currentTrack)) {
|
|
// If we change the active track while a cue is already displayed we need to update it
|
|
if (Array.from(currentTrack.activeCues || []).length) {
|
|
captions.setCue.call(this, currentTrack);
|
|
}
|
|
}
|
|
} else if (this.isVimeo && this.captions.active) {
|
|
this.embed.enableTextTrack(this.language);
|
|
}
|
|
},
|
|
|
|
// Get the tracks
|
|
getTracks() {
|
|
// Return empty array at least
|
|
if (utils.is.nullOrUndefined(this.media)) {
|
|
return [];
|
|
}
|
|
|
|
// Only get accepted kinds
|
|
return Array.from(this.media.textTracks || []).filter(track => ['captions', 'subtitles'].includes(track.kind));
|
|
},
|
|
|
|
// Get the current track for the current language
|
|
getCurrentTrack() {
|
|
return captions.getTracks.call(this).find(track => track.language.toLowerCase() === this.language);
|
|
},
|
|
|
|
// Display active caption if it contains text
|
|
setCue(input) {
|
|
// Get the track from the event if needed
|
|
const track = utils.is.event(input) ? input.target : input;
|
|
const active = track.activeCues[0];
|
|
const currentTrack = captions.getCurrentTrack.call(this);
|
|
|
|
// Only display current track
|
|
if (track !== currentTrack) {
|
|
return;
|
|
}
|
|
|
|
// Display a cue, if there is one
|
|
if (utils.is.cue(active)) {
|
|
captions.setText.call(this, active.getCueAsHTML());
|
|
} else {
|
|
captions.setText.call(this, null);
|
|
}
|
|
|
|
utils.dispatchEvent.call(this, this.media, 'cuechange');
|
|
},
|
|
|
|
// Set the current caption
|
|
setText(input) {
|
|
// Requires UI
|
|
if (!this.supported.ui) {
|
|
return;
|
|
}
|
|
|
|
if (utils.is.htmlElement(this.elements.captions)) {
|
|
const content = utils.createElement('span');
|
|
|
|
// Empty the container
|
|
utils.emptyElement(this.elements.captions);
|
|
|
|
// Default to empty
|
|
const caption = !utils.is.nullOrUndefined(input) ? input : '';
|
|
|
|
// Set the span content
|
|
if (utils.is.string(caption)) {
|
|
content.textContent = caption.trim();
|
|
} else {
|
|
content.appendChild(caption);
|
|
}
|
|
|
|
// Set new caption text
|
|
this.elements.captions.appendChild(content);
|
|
} else {
|
|
this.console.warn('No captions element to render to');
|
|
}
|
|
},
|
|
|
|
// Display captions container and button (for initialization)
|
|
show() {
|
|
// If there's no caption toggle, bail
|
|
if (!utils.is.htmlElement(this.elements.buttons.captions)) {
|
|
return;
|
|
}
|
|
|
|
// Try to load the value from storage
|
|
let active = storage.get.call(this).captions;
|
|
|
|
// Otherwise fall back to the default config
|
|
if (!utils.is.boolean(active)) {
|
|
({ active } = this.config.captions);
|
|
} else {
|
|
this.captions.active = active;
|
|
}
|
|
|
|
if (active) {
|
|
utils.toggleClass(this.elements.container, this.config.classNames.captions.active, true);
|
|
utils.toggleState(this.elements.buttons.captions, true);
|
|
}
|
|
},
|
|
};
|
|
|
|
export default captions;
|