media-controller.js [plain text]
class MediaController
{
constructor(shadowRoot, media, host)
{
this.shadowRoot = shadowRoot;
this.media = media;
this.host = host;
this.fullscreenChangeEventType = media.webkitSupportsPresentationMode ? "webkitpresentationmodechanged" : "webkitfullscreenchange";
this.hasPlayed = false;
this.container = shadowRoot.appendChild(document.createElement("div"));
this.container.className = "media-controls-container";
this._updateControlsIfNeeded();
this._usesLTRUserInterfaceLayoutDirection = false;
if (host) {
host.controlsDependOnPageScaleFactor = this.layoutTraits & LayoutTraits.iOS;
this.container.insertBefore(host.textTrackContainer, this.controls.element);
if (host.isInMediaDocument)
this.mediaDocumentController = new MediaDocumentController(this);
}
scheduler.flushScheduledLayoutCallbacks();
shadowRoot.addEventListener("resize", this);
media.videoTracks.addEventListener("addtrack", this);
media.videoTracks.addEventListener("removetrack", this);
media.addEventListener(this.fullscreenChangeEventType, this);
}
get isAudio()
{
if (this.media instanceof HTMLAudioElement)
return true;
if (this.host && !this.host.isInMediaDocument && this.media instanceof HTMLVideoElement)
return false;
if (this.media.readyState < HTMLMediaElement.HAVE_METADATA)
return false;
if (this.media.videoWidth || this.media.videoHeight)
return false;
return !this.media.videoTracks.length;
}
get isYouTubeEmbedWithTitle()
{
const url = new URL(this.media.ownerDocument.defaultView.location.href);
return url.href.includes("youtube.com/embed/") && url.searchParams.get("showinfo") !== "0";
}
get isFullscreen()
{
return this.media.webkitSupportsPresentationMode ? this.media.webkitPresentationMode === "fullscreen" : this.media.webkitDisplayingFullscreen;
}
get layoutTraits()
{
let traits = window.navigator.platform === "MacIntel" ? LayoutTraits.macOS : LayoutTraits.iOS;
if (this.isFullscreen)
return traits | LayoutTraits.Fullscreen;
return traits;
}
togglePlayback()
{
if (this.media.paused)
this.media.play();
else
this.media.pause();
}
set pageScaleFactor(pageScaleFactor)
{
this.controls.scaleFactor = pageScaleFactor;
this._updateControlsSize();
}
set usesLTRUserInterfaceLayoutDirection(flag)
{
if (this._usesLTRUserInterfaceLayoutDirection === flag)
return;
this._usesLTRUserInterfaceLayoutDirection = flag;
this.controls.usesLTRUserInterfaceLayoutDirection = flag;
}
mediaControlsFadedStateDidChange()
{
this._updateTextTracksClassList();
}
macOSControlsBackgroundWasClicked()
{
if (this.media.controls)
this.togglePlayback();
}
iOSInlineMediaControlsRecognizedTapGesture()
{
if (this.media.controls)
this.media.play();
}
iOSInlineMediaControlsRecognizedPinchInGesture()
{
this.media.webkitEnterFullscreen();
}
handleEvent(event)
{
if (event instanceof TrackEvent && event.currentTarget === this.media.videoTracks)
this._updateControlsIfNeeded();
else if (event.type === "resize" && event.currentTarget === this.shadowRoot) {
this._updateControlsIfNeeded();
scheduler.flushScheduledLayoutCallbacks();
} else if (event.currentTarget === this.media) {
this._updateControlsIfNeeded();
if (event.type === "webkitpresentationmodechanged")
this._returnMediaLayerToInlineIfNeeded();
}
}
_updateControlsIfNeeded()
{
const layoutTraits = this.layoutTraits;
const previousControls = this.controls;
const ControlsClass = this._controlsClassForLayoutTraits(layoutTraits);
if (previousControls && previousControls.constructor === ControlsClass) {
this._updateTextTracksClassList();
this._updateControlsSize();
return;
}
if (this._supportingObjects) {
for (let supportingObject of this._supportingObjects)
supportingObject.destroy();
}
this.controls = new ControlsClass;
this.controls.delegate = this;
if (this.shadowRoot.host && this.shadowRoot.host.dataset.autoHideDelay)
this.controls.bottomControlsBar.autoHideDelay = this.shadowRoot.host.dataset.autoHideDelay;
if (previousControls) {
this.controls.fadeIn();
this.container.replaceChild(this.controls.element, previousControls.element);
this.controls.usesLTRUserInterfaceLayoutDirection = previousControls.usesLTRUserInterfaceLayoutDirection;
} else
this.container.appendChild(this.controls.element);
this._updateTextTracksClassList();
this._updateControlsSize();
this._supportingObjects = [AirplaySupport, AudioSupport, ControlsVisibilitySupport, FullscreenSupport, MuteSupport, PiPSupport, PlacardSupport, PlaybackSupport, ScrubbingSupport, SeekBackwardSupport, SeekForwardSupport, SkipBackSupport, SkipForwardSupport, StartSupport, StatusSupport, TimeControlSupport, TracksSupport, VolumeSupport, VolumeDownSupport, VolumeUpSupport].map(SupportClass => {
return new SupportClass(this);
}, this);
this.controls.shouldUseSingleBarLayout = this.controls instanceof InlineMediaControls && this.isYouTubeEmbedWithTitle;
}
_updateControlsSize()
{
this.controls.width = this._controlsWidth();
this.controls.height = Math.round(this.container.getBoundingClientRect().height * this.controls.scaleFactor);
this.controls.shouldCenterControlsVertically = this.isAudio;
}
_controlsWidth()
{
return Math.round(this.container.getBoundingClientRect().width * (this.controls ? this.controls.scaleFactor : 1));
}
_returnMediaLayerToInlineIfNeeded()
{
if (this.host)
window.requestAnimationFrame(() => this.host.setPreparedToReturnVideoLayerToInline(this.media.webkitPresentationMode !== PiPMode));
}
_controlsClassForLayoutTraits(layoutTraits)
{
if (layoutTraits & LayoutTraits.iOS)
return IOSInlineMediaControls;
if (layoutTraits & LayoutTraits.Fullscreen)
return MacOSFullscreenMediaControls;
return MacOSInlineMediaControls;
}
_updateTextTracksClassList()
{
if (!this.host)
return;
const layoutTraits = this.layoutTraits;
if (layoutTraits & LayoutTraits.Fullscreen)
return;
this.host.textTrackContainer.classList.toggle("visible-controls-bar", !this.controls.faded);
}
}