@ebay/ebayui-core
Version:
Collection of core eBay components; considered to be the building blocks for all composite structures, pages & apps.
394 lines (393 loc) • 14.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const shaka_player_1 = require("@internal/shaka-player");
const elements_1 = require("./elements");
const DEFAULT_SPINNER_TIMEOUT = 2000;
const eventList = [
"abort",
"canplay",
"canplaythrough",
"durationchange",
"emptied",
"encrypted",
"ended",
"error",
"loadstart",
"progress",
"ratechange",
"seeked",
"seeking",
"stalled",
"suspend",
"timeupdate",
"waiting",
];
const defaultControlPanelElements = [
"play_pause",
"current_time",
"spacer",
"total_time",
"captions",
"mute_popover",
"report",
"fullscreen_button",
];
const compactLayoutControlPanelElements = [
"remaining_time",
"spacer",
"mute_popover",
"play_pause",
];
const videoConfig = {
doubleClickForFullscreen: true,
singleClickForPlayAndPause: true,
customContextMenu: true,
contextMenuElements: ["mute"],
showUIAlways: false,
addSeekBar: true,
controlPanelElements: defaultControlPanelElements,
};
const compactConfig = {
doubleClickForFullscreen: true,
singleClickForPlayAndPause: true,
customContextMenu: true,
contextMenuElements: ["mute"],
showUIAlways: false,
addSeekBar: false,
controlPanelElements: compactLayoutControlPanelElements,
};
class Video {
isPlaylist(source) {
const type = source.type && source.type.toLowerCase();
const src = source.src;
if (type === "dash" || type === "hls") {
return true;
}
else if (source.src) {
return (src.indexOf(".mpd") === src.length - 5 ||
src.indexOf(".m3u8") === src.length - 6);
}
return false;
}
handleResize() {
if (!this.input.width && this.video && this.root) {
const { width: containerWidth } = this.root.getBoundingClientRect();
this.containerEl.setAttribute("width", containerWidth.toString());
this.alignSeekbar();
}
}
alignSeekbar() {
if (this.el) {
const buttonPanel = this.el.querySelector(".shaka-controls-button-panel");
const spacer = buttonPanel.querySelector(".shaka-spacer");
const rangeContainer = this.el.querySelector(".shaka-range-container");
if (buttonPanel && spacer && rangeContainer) {
const buttonPanelRect = buttonPanel.getBoundingClientRect();
const spacerRect = spacer.getBoundingClientRect();
rangeContainer.style.marginRight = `${buttonPanelRect.right - spacerRect.right}px`;
rangeContainer.style.marginLeft = `${spacerRect.left - buttonPanelRect.left}px`;
}
}
}
handlePause(originalEvent) {
// On IOS, the controls force showing up if the video exist fullscreen while playing.
// This forces the controls to always hide
this.video.controls = false;
if (!this.isAutoPause) {
this.userPaused = true;
}
this.emit("pause", {
originalEvent,
player: this.player,
auto: this.isAutoPause,
});
// Reset isAutoPause after emitting the event
this.isAutoPause = false;
this.alignSeekbar();
}
handlePlaying(originalEvent) {
this.showControls();
this.alignSeekbar();
if (this.input.playView === "fullscreen" &&
this.input.layout !== "compact") {
this.video.requestFullscreen();
}
this.state.played = true;
this.userPaused = false;
this.emit("play", {
originalEvent,
player: this.player,
auto: this.isAutoPlay,
});
// Reset isAutoPlay after emitting the event
this.isAutoPlay = false;
}
handleVolumeChange(originalEvent) {
this.emit("volume-change", {
originalEvent,
volume: this.video.volume,
muted: this.video.muted,
});
}
handleError(err) {
this.state.failed = true;
this.playButtonContainer.remove();
this.emit("load-error", err);
}
showControls() {
let copyConfig = Object.assign({}, videoConfig);
copyConfig.controlPanelElements = [...videoConfig.controlPanelElements];
if (this.input.layout === "compact") {
copyConfig = Object.assign({}, compactConfig);
}
if (this.input.nav) {
copyConfig.doubleClickForFullscreen = false;
copyConfig.singleClickForPlayAndPause = false;
copyConfig.showUIAlways = true;
}
if (this.state.volumeSlider === true) {
const insertAt = copyConfig.controlPanelElements.length - 2 > 0
? copyConfig.controlPanelElements.length - 2
: copyConfig.controlPanelElements.length;
copyConfig.controlPanelElements.splice(insertAt, 0, "volume");
}
this.ui.configure(copyConfig);
this.video.controls = false;
}
takeAction() {
switch (this.state.action) {
case "play":
this.video.play();
break;
case "pause":
this.video.pause();
break;
default:
}
}
onInput(input) {
var _a, _b;
if (this.video) {
if (input.width || input.height) {
this.containerEl.style.width = `${input.width}px`;
}
this.video.volume = (_a = input.volume) !== null && _a !== void 0 ? _a : 0;
this.video.muted = !!input.muted;
}
// Check if action is changed
if (this.state.action !== input.action) {
this.state.action = (_b = input.action) !== null && _b !== void 0 ? _b : "";
this.takeAction();
}
if (input.volumeSlider === true) {
this.state.volumeSlider = input.volumeSlider;
}
}
onCreate(input) {
this.state = {
volumeSlider: false,
action: "",
failed: false,
played: false,
};
if (input.action === "play" || input.autoplay === true) {
this.isAutoPlay = true;
}
}
_loadSrc(index) {
const currentIndex = index || 0;
const sources = [...this.input.source];
const src = sources[currentIndex];
let nextIndex;
if (src && sources.length > currentIndex + 1) {
nextIndex = currentIndex + 1;
}
this.player
.load(src.src)
.then(() => {
this.state.failed = false;
return Promise.all((this.input.clip || []).map((track) => {
return this.player.addTextTrack(track.src, track.srclang, track.kind);
}));
})
.then(() => {
const [track] = this.player.getTextTracks();
if (track) {
this.player.selectTextTrack(track.id); // => this finds the id and everythings fine but it does nothing
}
})
.catch((err) => {
if (nextIndex) {
setTimeout(() => this._loadSrc(nextIndex), 0);
}
else {
this.handleError(err);
}
});
}
_attach() {
var _a;
const { Report, CurrentTime, RemainingTime, TotalTime, MuteButton, FullscreenButton, TextSelection, } = (0, elements_1.getElements)(this);
// eslint-disable-next-line no-undef,new-cap
this.ui = new this.shaka.ui.Overlay(this.player, this.containerEl, this.video);
if ((_a = document === null || document === void 0 ? void 0 : document.documentElement) === null || _a === void 0 ? void 0 : _a.lang) {
this.ui
.getControls()
.getLocalization()
.changeLocale([document.documentElement.lang]);
}
// eslint-disable-next-line no-undef,new-cap
this.shaka.ui.Controls.registerElement("report", new Report.Factory());
// eslint-disable-next-line no-undef,new-cap
this.shaka.ui.Controls.registerElement("current_time", new CurrentTime.Factory());
this.shaka.ui.Controls.registerElement("remaining_time", new RemainingTime.Factory());
// eslint-disable-next-line no-undef,new-cap
this.shaka.ui.Controls.registerElement("total_time", new TotalTime.Factory());
// eslint-disable-next-line no-undef,new-cap
this.shaka.ui.Controls.registerElement("mute_popover", new MuteButton.Factory());
// eslint-disable-next-line no-undef,new-cap
this.shaka.ui.Controls.registerElement("fullscreen_button", new FullscreenButton.Factory());
// eslint-disable-next-line no-undef,new-cap
this.shaka.ui.Controls.registerElement("captions", new TextSelection.Factory());
this.ui.configure({
controlPanelElements: [],
addSeekBar: false,
});
// Replace play icon
if (this.el) {
const playIcon = this.getComponent("play-icon").el.cloneNode(true);
const container = this.el.querySelector(".shaka-controls-container");
this.playButtonContainer = document.createElement("div");
this.playButtonContainer.classList.add("shaka-play-button-container");
this.playButtonContainer.appendChild(playIcon);
container.appendChild(this.playButtonContainer);
const shakaSpinner = this.el.querySelector(".shaka-spinner");
if (shakaSpinner) {
setTimeout(() => {
shakaSpinner.hidden = true;
}, this.input.spinnerTimeout || DEFAULT_SPINNER_TIMEOUT);
}
}
}
handleSuccess() {
// eslint-disable-next-line no-undef,new-cap
this.shaka.polyfill.installAll();
// eslint-disable-next-line no-undef,new-cap
this.player = new this.shaka.Player(this.video);
this.player.configure(this.input.shakaConfig || {});
this._attach();
this._loadSrc();
}
onMount() {
this.root = this.getEl("root");
this.video = this.root.querySelector("video");
this.containerEl = this.root.querySelector(".video-player__container");
this.video.volume = this.input.volume || 1;
this.video.muted = this.input.muted !== false;
this.subscribeTo(this.video)
.on("playing", this.handlePlaying.bind(this))
.on("pause", this.handlePause.bind(this))
.on("volumechange", this.handleVolumeChange.bind(this));
eventList.forEach((eventName) => {
this.subscribeTo(this.video).on(eventName, (e) => this.emit(eventName, e));
});
if (this.input.offscreenPause) {
// Set up Intersection Observer to detect when video is 50% in viewport
this.setupIntersectionObserver();
// Add mousedown listener to intercept focus events from user clicking on video
this.mouseDownHandler = this.handleMouseDown.bind(this);
this.root.addEventListener("mousedown", this.mouseDownHandler);
// Add window focus event listener to play video when window regains focus
this.windowFocusHandler = this.handleWindowFocus.bind(this);
window.addEventListener("focus", this.windowFocusHandler);
// Add window blur event listener to pause video when window loses focus
this.windowBlurHandler = this.handleWindowBlur.bind(this);
window.addEventListener("blur", this.windowBlurHandler);
}
this._loadVideo();
}
setupIntersectionObserver() {
const options = {
root: null,
rootMargin: "0px",
threshold: 0.5,
};
this.observer = new IntersectionObserver((entries) => {
// Auto-play when 50% visible and pause when less than 50% visible
entries.forEach((entry) => {
if (this.userPaused) {
// If user has manually paused, do not auto-play/pause
return;
}
if (entry.isIntersecting) {
this.isInViewport = true;
if (!this.state.failed && this.video.paused) {
this.isAutoPlay = true;
this.video.play().catch((e) => {
this.isAutoPlay = false;
});
}
}
else {
this.isInViewport = false;
if (!this.video.paused) {
this.isAutoPause = true;
this.video.pause();
}
}
});
}, options);
this.observer.observe(this.containerEl);
}
handleMouseDown() {
if (!document.hasFocus()) {
this.isFocusFromVideoClick = true;
}
}
handleWindowFocus() {
if (this.isFocusFromVideoClick) {
// Let the video click event handle play
this.isFocusFromVideoClick = false;
return;
}
if (this.isInViewport && this.video.paused && !this.userPaused) {
this.isAutoPlay = true;
this.video.play().catch((e) => {
this.isAutoPlay = false;
});
}
}
handleWindowBlur() {
if (!this.video.paused) {
this.isAutoPause = true;
this.video.pause();
}
}
onDestroy() {
if (this.ui) {
this.ui.destroy();
}
if (this.observer) {
this.observer.disconnect();
}
if (this.input.offscreenPause) {
this.root.removeEventListener("mousedown", this.mouseDownHandler);
window.removeEventListener("focus", this.windowFocusHandler);
window.removeEventListener("blur", this.windowBlurHandler);
}
}
_loadVideo() {
this.state.failed = false;
(0, shaka_player_1.load)()
.then((shaka) => {
this.shaka = shaka.default || shaka;
window.shaka = this.shaka; // Set global object for some components to access
this.handleSuccess();
})
.catch((e) => {
console.log(e);
this.handleError(e);
});
}
}
module.exports = Video;