// ==========================================================================
// Plyr UI
// ==========================================================================
import utils from './utils';
import captions from './captions';
import controls from './controls';
import fullscreen from './fullscreen';
import listeners from './listeners';
import storage from './storage';
const ui = {
addStyleHook() {
utils.toggleClass(this.elements.container, this.config.selectors.container.replace('.', ''), true);
utils.toggleClass(this.elements.container, this.config.classNames.uiSupported, this.supported.ui);
},
// Toggle native HTML5 media controls
toggleNativeControls(toggle) {
if (toggle && this.isHTML5) {
this.media.setAttribute('controls', '');
} else {
this.media.removeAttribute('controls');
}
},
// Setup the UI
build() {
// Re-attach media element listeners
// TODO: Use event bubbling
listeners.media.call(this);
// Don't setup interface if no support
if (!this.supported.ui) {
this.warn(`Basic support only for ${this.type}`);
// Remove controls
utils.removeElement.call(this, 'controls');
// Remove large play
utils.removeElement.call(this, 'buttons.play');
// Restore native controls
ui.toggleNativeControls.call(this, true);
// Bail
return;
}
// Inject custom controls if not present
if (!utils.is.htmlElement(this.elements.controls)) {
// Inject custom controls
controls.inject.call(this);
// Re-attach control listeners
listeners.controls.call(this);
}
// If there's no controls, bail
if (!utils.is.htmlElement(this.elements.controls)) {
return;
}
// Remove native controls
ui.toggleNativeControls.call(this);
// Setup fullscreen
fullscreen.setup.call(this);
// Captions
captions.setup.call(this);
// Set volume
this.volume = null;
ui.updateVolume.call(this);
// Set playback speed
this.speed = null;
// Set loop
// this.setLoop();
// Reset time display
ui.timeUpdate.call(this);
// Update the UI
ui.checkPlaying.call(this);
this.ready = true;
// Ready event at end of execution stack
utils.dispatchEvent.call(this, this.media, 'ready');
// Autoplay
if (this.config.autoplay) {
this.play();
}
},
// Show the duration on metadataloaded
displayDuration() {
if (!this.supported.ui) {
return;
}
// If there's only one time display, display duration there
if (!this.elements.display.duration && this.config.displayDuration && this.media.paused) {
ui.updateTimeDisplay.call(this, this.duration, this.elements.display.currentTime);
}
// If there's a duration element, update content
if (this.elements.display.duration) {
ui.updateTimeDisplay.call(this, this.duration, this.elements.display.duration);
}
// Update the tooltip (if visible)
ui.updateSeekTooltip.call(this);
},
// Setup aria attribute for play and iframe title
setTitle() {
// Find the current text
let label = this.config.i18n.play;
// If there's a media title set, use that for the label
if (utils.is.string(this.config.title) && !utils.is.empty(this.config.title)) {
label += `, ${this.config.title}`;
// Set container label
this.elements.container.setAttribute('aria-label', this.config.title);
}
// If there's a play button, set label
if (this.supported.ui) {
if (utils.is.htmlElement(this.elements.buttons.play)) {
this.elements.buttons.play.setAttribute('aria-label', label);
}
if (utils.is.htmlElement(this.elements.buttons.playLarge)) {
this.elements.buttons.playLarge.setAttribute('aria-label', label);
}
}
// Set iframe title
// https://github.com/sampotts/plyr/issues/124
if (this.isEmbed) {
const iframe = utils.getElement.call(this, 'iframe');
if (!utils.is.htmlElement(iframe)) {
return;
}
// Default to media type
const title = !utils.is.empty(this.config.title) ? this.config.title : 'video';
iframe.setAttribute('title', this.config.i18n.frameTitle.replace('{title}', title));
}
},
// Check playing state
checkPlaying() {
utils.toggleClass(this.elements.container, this.config.classNames.playing, !this.media.paused);
utils.toggleClass(this.elements.container, this.config.classNames.stopped, this.media.paused);
this.toggleControls(this.media.paused);
},
// Update volume UI and storage
updateVolume() {
// Update the if present
if (this.supported.ui) {
const value = this.media.muted ? 0 : this.media.volume;
if (this.elements.inputs.volume) {
ui.setRange.call(this, this.elements.inputs.volume, value);
}
}
// Update the volume in storage
storage.set.call(this, {
volume: this.media.volume,
});
// Toggle class if muted
utils.toggleClass(this.elements.container, this.config.classNames.muted, this.media.muted);
// Update checkbox for mute state
if (this.supported.ui && this.elements.buttons.mute) {
utils.toggleState(this.elements.buttons.mute, this.media.muted);
}
},
// Check if media is loading
checkLoading(event) {
this.loading = event.type === 'waiting';
// Clear timer
clearTimeout(this.timers.loading);
// Timer to prevent flicker when seeking
this.timers.loading = setTimeout(() => {
// Toggle container class hook
utils.toggleClass(this.elements.container, this.config.classNames.loading, this.loading);
// Show controls if loading, hide if done
this.toggleControls(this.loading);
}, this.loading ? 250 : 0);
},
// Update seek value and lower fill
setRange(target, value) {
if (!utils.is.htmlElement(target)) {
return;
}
target.value = value;
// Webkit range fill
controls.updateRangeFill.call(this, target);
},
// Set