chicken-player
Version:
JavaScript plugin for YouTube, Vimeo, and Dailymotion video embedding
586 lines (585 loc) • 19.9 kB
JavaScript
class d {
constructor() {
this.apiReady = !1, this.tempPlayerUid = null, this.tempPlayerId = null, this.videos = [], this.timers = [], this.config = {};
}
/**
* Initialize the player with configuration
* @param {string} id - Video ID
* @param {string} uid - Player unique ID
* @param {Object} config - Player configuration
*/
initPlayer(e, t, i) {
this.apiReady ? this.attemptPlayer(t, e) : (this.tempPlayerUid = t, this.tempPlayerId = e, this.config = {
needConsent: !1,
...i
}, this.initApi());
}
/**
* Initialize the video API
* @abstract
*/
initApi() {
throw new Error("initApi must be implemented by child class");
}
/**
* Create a new player instance
* @param {string} uid - Player unique ID
* @param {string} id - Video ID
* @abstract
*/
createPlayer(e, t) {
throw new Error("createPlayer must be implemented by child class");
}
/**
* Attempt to create or start the player
* @param {string} uid - Player unique ID
* @param {string} id - Video ID
*/
attemptPlayer(e, t) {
this.videos[e] ? setTimeout(() => {
this.startPlayer(e);
}, 1500) : this.createPlayer(e, t);
}
/**
* Stop the player
* @param {string} uid - Player unique ID
*/
stopPlayer(e) {
document.querySelector(`#${e}`).dispatchEvent(this.config.events.stop), this.videos[e].pause();
}
/**
* Start the player
* @param {string} uid - Player unique ID
*/
startPlayer(e) {
document.querySelector(`#${e}`).dispatchEvent(this.config.events.play), this.videos[e].play();
}
/**
* Handle player ready state
* @param {string} uid - Player unique ID
*/
onPlayerReady(e) {
const t = document.querySelector(`#${e}`), i = `.${this.config.classes.wrapper}`, s = t.closest(i);
t.addEventListener("chickenPlayer.play", () => {
s.classList.remove(this.config.classes.stateLoading), s.classList.add(this.config.classes.statePlaying);
}), t.addEventListener("chickenPlayer.stop", () => {
s.classList.remove(this.config.classes.statePlaying);
}), this.startPlayer(e);
}
/**
* Handle player state changes
* @param {string} uid - Player unique ID
*/
onPlayerStateChange(e) {
const t = document.querySelector(`#${e}`);
this.videos[e].on("pause", () => {
this.timers[e] = setTimeout(() => {
t.dispatchEvent(this.config.events.stop);
}, 1e3);
}), this.videos[e].on("play", () => {
clearTimeout(this.timers[e]), t.dispatchEvent(this.config.events.play);
});
}
}
class f extends d {
/**
* Initialize the Dailymotion API
*/
initApi() {
if (!this.config.player.dailymotion.playerId)
return console.error("Dailymotion player ID is not set"), !1;
const e = document.createElement("script");
e.src = `https://geo.dailymotion.com/libs/player/${this.config.player.dailymotion.playerId}.js`;
const t = document.getElementsByTagName("script")[0];
t.parentNode.insertBefore(e, t);
}
/**
* Create a new Dailymotion player instance
* @param {string} uid - Player unique ID
* @param {string} id - Video ID
*/
createPlayer(e, t) {
this.videos[e] || dailymotion.createPlayer(e, {
video: t,
...this.config.player.dailymotion
}).then((i) => {
this.videos[e] = i, this.onPlayerReady(e), this.onPlayerStateChange(e);
});
}
}
const r = new f();
window.dailymotion === void 0 && (window.dailymotion = {
onScriptLoaded: () => {
r.apiReady = !0, r.attemptPlayer(
r.tempPlayerUid,
r.tempPlayerId
);
}
});
class v extends d {
/**
* Initialize the YouTube API
*/
initApi() {
const e = document.createElement("script");
e.src = "//www.youtube.com/iframe_api";
const t = document.getElementsByTagName("script")[0];
t.parentNode.insertBefore(e, t);
}
/**
* Create a new YouTube player instance
* @param {string} uid - Player unique ID
* @param {string} id - Video ID
*/
createPlayer(e, t) {
this.videos[e] || (this.videos[e] = new YT.Player(e, {
height: this.config.player.height,
width: this.config.player.width,
videoId: t,
host: this.config.player.youtube.host,
events: {
onReady: () => this.onPlayerReady(e),
onStateChange: (i) => this.onYouTubeStateChange(e, i)
},
...this.config.player.youtube
}));
}
/**
* Handle YouTube specific state changes
* @param {string} uid - Player unique ID
* @param {Object} event - YouTube player event
*/
onYouTubeStateChange(e, t) {
const i = document.querySelector(`#${e}`);
(t.data === 0 || t.data === 2) && (this.timers[e] = setTimeout(() => {
i.dispatchEvent(this.config.events.stop);
}, 1e3)), t.data === 1 && (clearTimeout(this.timers[e]), i.dispatchEvent(this.config.events.play));
}
/**
* Stop the YouTube player
* @param {string} uid - Player unique ID
*/
stopPlayer(e) {
document.querySelector(`#${e}`).dispatchEvent(this.config.events.stop), this.videos[e].stopVideo();
}
/**
* Start the YouTube player
* @param {string} uid - Player unique ID
*/
startPlayer(e) {
document.querySelector(`#${e}`).dispatchEvent(this.config.events.play), this.videos[e].playVideo();
}
}
const l = new v();
window.onYouTubeIframeAPIReady = function() {
l.apiReady = !0, l.attemptPlayer(
l.tempPlayerUid,
l.tempPlayerId
);
};
class b extends d {
/**
* Initialize the Vimeo API
*/
initApi() {
const e = document.createElement("script");
e.src = "//player.vimeo.com/api/player.js", e.onload = window.onVimeoReadyCallback;
const t = document.getElementsByTagName("script")[0];
t.parentNode.insertBefore(e, t);
}
/**
* Create a new Vimeo player instance
* @param {string} uid - Player unique ID
* @param {string} id - Video ID
*/
createPlayer(e, t) {
this.videos[e] || (this.videos[e] = new Vimeo.Player(e, {
height: this.config.player.height,
width: this.config.player.width,
id: t,
...this.config.player.vimeo
}), this.onPlayerReady(e), this.onPlayerStateChange(e));
}
}
const p = new b();
window.onVimeoReadyCallback = function() {
p.apiReady = !0, p.attemptPlayer(
p.tempPlayerUid,
p.tempPlayerId
);
};
class P extends d {
/**
* Initialize the HTML5 player
* No API initialization needed for HTML5
*/
initApi() {
this.apiReady = !0, this.attemptPlayer(
this.tempPlayerUid,
this.tempPlayerId
);
}
/**
* Create a new HTML5 player instance
* @param {string} uid - Player unique ID
* @param {string} id - Video URL
*/
createPlayer(e, t) {
if (!this.videos[e]) {
const i = document.createElement("video");
i.id = e, i.className = "html5-player";
const s = this.config.player.html5;
i.controls = s.controls, i.preload = s.preload, i.autoplay = s.autoplay, i.loop = s.loop, i.muted = s.muted, i.width = s.width, i.height = s.height, i.setAttribute("playsinline", ""), i.setAttribute("webkit-playsinline", ""), s.poster && i.setAttribute("poster", s.poster), s.crossorigin && i.setAttribute("crossorigin", s.crossorigin), s.disablePictureInPicture && i.setAttribute("disablePictureInPicture", ""), s.disableRemotePlayback && i.setAttribute("disableRemotePlayback", ""), s.controlsList && i.setAttribute("controlsList", s.controlsList);
const o = document.createElement("source");
o.src = t, o.type = this.getVideoType(t), i.appendChild(o);
const n = document.querySelector(`#${e}`);
n && n.parentNode.replaceChild(i, n), this.videos[e] = i, this.onPlayerReady(e), this.onPlayerStateChange(e);
}
}
/**
* Get video MIME type from URL
* @param {string} url - Video URL
* @returns {string} Video MIME type
*/
getVideoType(e) {
const t = e.split(".").pop().toLowerCase();
return {
mp4: "video/mp4",
webm: "video/webm",
ogg: "video/ogg",
mov: "video/quicktime",
m4v: "video/x-m4v"
}[t] || "video/mp4";
}
/**
* Stop the HTML5 player
* @param {string} uid - Player unique ID
*/
stopPlayer(e) {
document.querySelector(`#${e}`).dispatchEvent(this.config.events.stop), this.videos[e].pause();
}
/**
* Start the HTML5 player
* @param {string} uid - Player unique ID
*/
startPlayer(e) {
document.querySelector(`#${e}`).dispatchEvent(this.config.events.play), this.videos[e].play();
}
/**
* Handle player state changes
* @param {string} uid - Player unique ID
*/
onPlayerStateChange(e) {
const t = document.querySelector(`#${e}`);
this.videos[e].addEventListener("pause", () => {
this.timers[e] = setTimeout(() => {
t.dispatchEvent(this.config.events.stop);
}, 1e3);
}), this.videos[e].addEventListener("play", () => {
clearTimeout(this.timers[e]), t.dispatchEvent(this.config.events.play);
});
}
}
const u = new P();
class w {
constructor(e, t) {
this.consentState = !1, this.needConsent = t.cookies.active, this.consentEvent = t.cookies.eventConsent, this.rejectEvent = t.cookies.eventReject, this.consentState = !this.needConsent, this.config = t, this.playerType = e, this.wrapper = document.querySelector(`.${t.classes.wrapper}`), this.setPlayerConsent(), this.needConsent && (this.setConsentMessage(), this.setWrapperState()), this.needConsent && this.consentEvent && window.addEventListener(this.consentEvent, () => {
this.consentState = !0, this.setWrapperState();
}), this.needConsent && this.rejectEvent && window.addEventListener(this.rejectEvent, () => {
this.consentState = !1, this.setWrapperState();
});
}
setWrapperState() {
this.consentState ? this.wrapper.classList.remove(this.config.classes.needConsent) : this.wrapper.classList.add(this.config.classes.needConsent);
}
setConsentMessage() {
const e = this.wrapper.querySelector(`.${this.config.classes.cover}`);
if (!e) return;
const t = document.createElement("div");
t.className = this.config.classes.consentMessage, t.innerHTML = `
<p>${this.config.cookies.message}</p>
`, e.appendChild(t);
}
setPlayerConsent() {
var e, t, i, s, o, n;
(t = (e = this.config.player[this.playerType]) == null ? void 0 : e.cookies) != null && t.active && (this.needConsent = this.config.player[this.playerType].cookies.active, this.wrapper = document.querySelector(`.${this.config.classes.wrapper}.player--${this.playerType}`)), (s = (i = this.config.player[this.playerType]) == null ? void 0 : i.cookies) != null && s.eventConsent && (this.consentEvent = this.config.player[this.playerType].cookies.eventConsent), (n = (o = this.config.player[this.playerType]) == null ? void 0 : o.cookies) != null && n.eventReject && (this.rejectEvent = this.config.player[this.playerType].cookies.eventReject), this.consentState = !this.needConsent;
}
hasConsent() {
return this.consentState;
}
}
const k = {
selector: ".chicken-player",
player: {
width: 600,
height: 400,
/* Youtube defaults */
/* See: https://developers.google.com/youtube/player_parameters */
youtube: {
host: "https://www.youtube-nocookie.com",
playerVars: {
modestbranding: 1,
showinfo: 0,
controls: 1,
iv_load_policy: 3,
fs: 1,
rel: 0,
loop: 0,
mute: 0
}
},
/* Vimeo defaults */
/* See: https://developer.vimeo.com/player/sdk/embed */
vimeo: {
muted: !1,
loop: !1
},
/* Dailymotion defaults */
/* See: https://developers.dailymotion.com/guides/getting-started-with-web-sdk/ */
dailymotion: {
playerId: null,
params: {
startTime: 0,
scaleMode: "fit",
loop: !1,
mute: !1
}
},
/* HTML5 defaults */
/* See: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video */
html5: {
controls: !0,
preload: "auto",
playsinline: !1,
autoplay: !1,
loop: !1,
muted: !1,
poster: "",
width: "auto",
height: "auto",
crossorigin: "",
disablePictureInPicture: !1,
disableRemotePlayback: !1,
controlsList: ""
}
},
/* CSS Classes */
classes: {
wrapper: "cbo-chickenplayer",
object: "player-object",
cover: "player-cover",
button: "cover-button",
buttonIcon: "button-icon",
buttonSpinner: "button-spinner",
close: "player-close",
statePlaying: "player--playing",
stateLoading: "player--loading",
stateError: "player--error",
stateReady: "player--ready",
needConsent: "player--needconsent",
consentMessage: "cover-needconsent"
},
/* Picture */
picture: {
src: "https://unpkg.com/chicken-player/dist/placeholder.png",
width: 600,
height: 400
},
/* Events */
events: {
play: new Event("chickenPlayer.play"),
stop: new Event("chickenPlayer.stop")
},
/* Cookie Consent */
cookies: {
active: !1,
message: "Pour regarder cette vidéo, veuillez accepter les cookies du lecteur vidéo dans vos préférences de confidentialité.",
eventConsent: "chickenPlayer.cookies.consent",
eventReject: "chickenPlayer.cookies.reject",
types: ["youtube", "dailymotion", "vimeo"]
}
};
class S {
/**
* Initialize a new Chicken Player instance
* @param {Object} opts - User configuration options
*/
constructor(e = {}) {
this.config = this.mergeConfig(k, e), this.players = /* @__PURE__ */ new Map(), typeof window < "u" && typeof document < "u" && this.init();
}
/**
* Initialize the player with the current configuration
* Creates markup and binds events for all player elements
*/
init() {
document.querySelectorAll(`${this.config.selector}:not(.${this.config.classes.stateReady})`).forEach((e) => {
if (e.getAttribute("data-type") && e.getAttribute("data-id")) {
const t = this.createMarkup(e);
e.parentNode.replaceChild(t, e);
}
}), this.config.cookies.types.forEach((e) => {
new w(e, this.config);
}), this.bindEvents();
}
/**
* Deep merge two configuration objects
* @param {Object} defaultConfig - Default configuration
* @param {Object} userConfig - User provided configuration
* @returns {Object} Merged configuration
*/
mergeConfig(e, t) {
const i = { ...e };
for (const s in t)
t.hasOwnProperty(s) && (typeof t[s] == "object" && t[s] !== null && typeof e[s] == "object" && e[s] !== null ? i[s] = this.mergeConfig(e[s], t[s]) : i[s] = t[s]);
return i;
}
/**
* Create the player markup structure
* @param {HTMLElement} el - Original player element
* @returns {HTMLElement} Complete player markup
*/
createMarkup(e) {
let t = e.getAttribute("id");
t || (t = "chickenPlayer_" + Math.random().toString(36).substr(2, 9), e.setAttribute("id", t)), e.classList.add(this.config.classes.wrapper), e.classList.add("player--" + e.getAttribute("data-type"));
const i = this.createObject(t), s = this.createCover(e), o = this.createButton();
return e.appendChild(i), e.appendChild(s), s.appendChild(o), e;
}
createObject(e) {
const t = document.createElement("div");
return t.className = this.config.classes.object, t.setAttribute("id", e + "-object"), t;
}
/**
* Create the player cover element
* @param {HTMLElement} originalEl - Original player element
* @returns {HTMLElement} Cover element
*/
createCover(e) {
const t = document.createElement("div");
t.className = this.config.classes.cover;
const i = e.querySelector("img");
if (i)
t.appendChild(i);
else if (this.config.picture !== !1) {
const s = document.createElement("img");
s.src = this.config.picture.src, s.width = this.config.picture.width, s.height = this.config.picture.height, s.alt = "", s.setAttribute("loading", "lazy"), t.appendChild(s);
}
return t;
}
/**
* Create the play button element
* @returns {HTMLElement} Button element
*/
createButton() {
const e = document.createElement("button");
e.type = "button", e.className = this.config.classes.button;
const t = document.createElement("span");
t.className = this.config.classes.buttonIcon;
const i = document.createElement("div");
i.className = this.config.classes.buttonSpinner;
for (let s = 0; s < 4; s++) {
const o = document.createElement("div");
i.appendChild(o);
}
return e.appendChild(t), e.appendChild(i), e;
}
/**
* Bind event listeners to player elements
* Only binds events to elements that haven't been initialized yet
*/
bindEvents() {
const e = this.config.selector, t = `.${this.config.classes.object}`, i = `.${this.config.classes.button}`, s = `.${this.config.classes.close}`;
document.querySelectorAll(`${e}:not(.${this.config.classes.stateReady})`).forEach((o) => {
const n = o.querySelector(t), a = o.querySelector(i), h = o.querySelector(s);
if (!n || !a) {
console.warn("Chicken Player: Required elements not found for player", o);
return;
}
const y = o.getAttribute("data-type"), m = o.getAttribute("data-id"), g = n.getAttribute("id");
a.addEventListener("click", () => {
o.classList.add(this.config.classes.stateLoading), this.handlePlay(y, m, g);
}), h && h.addEventListener("click", () => {
o.classList.remove(this.config.classes.statePlaying), this.handleStop(y, m);
}), o.classList.add(this.config.classes.stateReady);
});
}
/**
* Handle play action based on player type
* @param {string} type - Player type (youtube, dailymotion, vimeo, html5)
* @param {string} id - Video ID or URL
* @param {string} uid - Player unique ID
*/
handlePlay(e, t, i) {
switch (e) {
case "youtube":
l.initPlayer(t, i, this.config);
break;
case "dailymotion":
r.initPlayer(t, i, this.config);
break;
case "vimeo":
p.initPlayer(t, i, this.config);
break;
case "html5":
u.initPlayer(t, i, this.config);
break;
default:
console.error("Unsupported player type:", e);
break;
}
}
/**
* Play all players or a specific player
* @param {string} [selector] - Optional CSS selector to target specific players
*/
play(e = null) {
const t = e || this.config.selector;
document.querySelectorAll(t).forEach((s) => {
const o = s.getAttribute("data-type"), n = s.getAttribute("data-id"), a = s.querySelector(`.${this.config.classes.object}`);
if (o && n && a) {
const h = a.getAttribute("id");
s.classList.add(this.config.classes.stateLoading), this.handlePlay(o, n, h);
}
});
}
/**
* Stop all players or a specific player
* @param {string} [selector] - Optional CSS selector to target specific players
*/
stop(e = null) {
const t = e || this.config.selector;
document.querySelectorAll(t).forEach((s) => {
const o = s.getAttribute("data-type"), n = s.querySelector(`.${this.config.classes.object}`);
if (o && n) {
const a = n.getAttribute("id");
s.classList.remove(this.config.classes.statePlaying), this.handleStop(o, a);
}
});
}
/**
* Handle stop action based on player type
* @param {string} type - Player type (youtube, dailymotion, vimeo, html5)
* @param {string} uid - Player unique ID
*/
handleStop(e, t) {
switch (e) {
case "youtube":
l.stopPlayer(t);
break;
case "dailymotion":
r.stopPlayer(t);
break;
case "vimeo":
p.stopPlayer(t);
break;
case "html5":
u.stopPlayer(t);
break;
default:
console.error("Unsupported player type:", e);
break;
}
}
}
export {
S as default
};