2024-01-24 21:53:26 +00:00
|
|
|
#include utility.js
|
|
|
|
#include uniqstr.js
|
2023-07-17 14:37:39 +00:00
|
|
|
#include watcher.js
|
|
|
|
|
2023-01-29 01:51:54 +00:00
|
|
|
const MszVideoEmbedPlayerEvents = function() {
|
|
|
|
return [
|
|
|
|
'play', 'pause', 'stop',
|
|
|
|
'mute', 'volume',
|
|
|
|
'rate', 'duration', 'time',
|
|
|
|
];
|
|
|
|
};
|
|
|
|
|
|
|
|
const MszVideoEstimateAspectRatio = function(width, height) {
|
|
|
|
const gcd = function(a, b) { return b ? gcd(b, a % b) : Math.abs(a); };
|
|
|
|
|
|
|
|
const ratio = gcd(width, height);
|
|
|
|
width /= ratio;
|
|
|
|
height /= ratio;
|
|
|
|
|
|
|
|
return [width, height];
|
|
|
|
};
|
|
|
|
|
|
|
|
const MszVideoConstrainSize = function(w, h, mw, mh) {
|
|
|
|
mw = mw || 0;
|
|
|
|
mh = mh || 0;
|
|
|
|
w = w || 0;
|
|
|
|
h = h || 0;
|
|
|
|
|
|
|
|
const ar = MszVideoEstimateAspectRatio(w, h);
|
|
|
|
|
|
|
|
if(w > h) {
|
|
|
|
if(mw > 0) {
|
|
|
|
w = Math.min(mw, w);
|
|
|
|
h = Math.ceil((w / ar[0]) * ar[1]);
|
|
|
|
if(mh > 0) h = Math.min(mh, h);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if(mh > 0) {
|
|
|
|
h = Math.min(mh, h);
|
|
|
|
w = Math.ceil((h / ar[1]) * ar[0]);
|
|
|
|
if(mw > 0) w = Math.min(mw, w);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return [w, h];
|
|
|
|
};
|
|
|
|
|
|
|
|
const MszVideoEmbed = function(playerOrFrame) {
|
|
|
|
const frame = playerOrFrame;
|
|
|
|
const player = 'getPlayer' in frame ? frame.getPlayer() : frame;
|
|
|
|
|
|
|
|
const elem = $e({
|
|
|
|
attrs: {
|
|
|
|
classList: ['embed', 'embed-' + player.getType()],
|
|
|
|
},
|
|
|
|
child: frame,
|
|
|
|
});
|
|
|
|
|
|
|
|
return {
|
|
|
|
getElement: function() {
|
|
|
|
return elem;
|
|
|
|
},
|
|
|
|
appendTo: function(target) {
|
|
|
|
target.appendChild(elem);
|
|
|
|
},
|
|
|
|
insertBefore: function(ref) {
|
|
|
|
$ib(ref, elem);
|
|
|
|
},
|
|
|
|
nuke: function() {
|
|
|
|
$r(elem);
|
|
|
|
},
|
|
|
|
replaceElement(target) {
|
|
|
|
$ib(target, elem);
|
|
|
|
$r(target);
|
|
|
|
},
|
|
|
|
getFrame: function() {
|
|
|
|
return frame;
|
|
|
|
},
|
|
|
|
getPlayer: function() {
|
|
|
|
return player;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
const MszVideoEmbedFrame = function(player, options) {
|
|
|
|
options = options || {};
|
|
|
|
|
2023-01-29 20:29:20 +00:00
|
|
|
const icoStatePlay = 'fa-play',
|
|
|
|
icoStatePause = 'fa-pause',
|
|
|
|
icoStateStop = 'fa-stop';
|
|
|
|
const icoVolMute = 'fa-volume-mute',
|
|
|
|
icoVolOff = 'fa-volume-off',
|
|
|
|
icoVolQuiet = 'fa-volume-down',
|
|
|
|
icoVolLoud = 'fa-volume-up';
|
|
|
|
|
|
|
|
const btnPlayPause = $e({
|
|
|
|
attrs: {},
|
|
|
|
child: {
|
|
|
|
tag: 'i',
|
|
|
|
attrs: {
|
|
|
|
classList: ['fas', 'fa-fw', icoStatePlay],
|
|
|
|
},
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const btnStop = $e({
|
|
|
|
attrs: {},
|
|
|
|
child: {
|
|
|
|
tag: 'i',
|
|
|
|
attrs: {
|
|
|
|
classList: ['fas', 'fa-fw', icoStateStop],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
const numCurrentTime = $e({
|
|
|
|
attrs: {},
|
|
|
|
});
|
|
|
|
|
|
|
|
const sldProgress = $e({
|
|
|
|
attrs: {},
|
|
|
|
child: [],
|
|
|
|
});
|
|
|
|
|
|
|
|
const numDurationRemaining = $e({
|
|
|
|
attrs: {},
|
|
|
|
});
|
|
|
|
|
|
|
|
const btnVolMute = $e({
|
|
|
|
attrs: {},
|
|
|
|
child: {
|
|
|
|
tag: 'i',
|
|
|
|
attrs: {
|
|
|
|
// isMuted === icoVolMute
|
|
|
|
// vol < 0.01 === icoVolOff
|
|
|
|
// vol < 0.5 === icoVolQuiet
|
|
|
|
// vol < 1.0 === icoVolLoud
|
|
|
|
classList: ['fas', 'fa-fw', icoVolLoud],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2023-01-29 01:51:54 +00:00
|
|
|
const elem = $e({
|
|
|
|
attrs: {
|
|
|
|
className: 'embedvf',
|
|
|
|
style: {
|
|
|
|
width: player.getWidth().toString() + 'px',
|
|
|
|
height: player.getHeight().toString() + 'px',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
child: [
|
|
|
|
{
|
|
|
|
attrs: {
|
|
|
|
className: 'embedvf-player',
|
|
|
|
},
|
|
|
|
child: player,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
attrs: {
|
|
|
|
className: 'embedvf-overlay',
|
|
|
|
},
|
|
|
|
child: [
|
|
|
|
{
|
|
|
|
attrs: {
|
|
|
|
className: 'embedvf-controls',
|
|
|
|
},
|
|
|
|
child: [
|
2023-01-29 20:29:20 +00:00
|
|
|
btnPlayPause,
|
|
|
|
btnStop,
|
|
|
|
numCurrentTime,
|
|
|
|
sldProgress,
|
|
|
|
numDurationRemaining,
|
2023-01-29 01:51:54 +00:00
|
|
|
],
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
],
|
|
|
|
});
|
|
|
|
|
|
|
|
return {
|
|
|
|
getElement: function() {
|
|
|
|
return elem;
|
|
|
|
},
|
|
|
|
appendTo: function(target) {
|
|
|
|
target.appendChild(elem);
|
|
|
|
},
|
|
|
|
insertBefore: function(ref) {
|
|
|
|
$ib(ref, elem);
|
|
|
|
},
|
|
|
|
nuke: function() {
|
|
|
|
$r(elem);
|
|
|
|
},
|
|
|
|
replaceElement(target) {
|
|
|
|
$ib(target, elem);
|
|
|
|
$r(target);
|
|
|
|
},
|
|
|
|
getPlayer: function() {
|
|
|
|
return player;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
const MszVideoEmbedPlayer = function(metadata, options) {
|
|
|
|
options = options || {};
|
|
|
|
|
|
|
|
const shouldAutoplay = options.autoplay === undefined || options.autoplay,
|
|
|
|
haveNativeControls = options.nativeControls !== undefined && options.nativeControls,
|
|
|
|
shouldObserveResize = options.observeResize === undefined || options.observeResize;
|
|
|
|
|
|
|
|
const videoAttrs = {
|
|
|
|
src: metadata.url,
|
|
|
|
style: {},
|
|
|
|
};
|
|
|
|
|
|
|
|
if(shouldAutoplay)
|
|
|
|
videoAttrs.autoplay = 'autoplay';
|
|
|
|
if(haveNativeControls)
|
|
|
|
videoAttrs.controls = 'controls';
|
|
|
|
|
|
|
|
const constrainSize = function(w, h, mw, mh) {
|
|
|
|
return MszVideoConstrainSize(w, h, mw || options.maxWidth, mh || options.maxHeight);
|
|
|
|
};
|
|
|
|
|
|
|
|
const initialSize = constrainSize(
|
|
|
|
options.width || metadata.width || 200,
|
|
|
|
options.height || metadata.height || 200
|
|
|
|
);
|
|
|
|
|
|
|
|
videoAttrs.style.width = initialSize[0].toString() + 'px';
|
|
|
|
videoAttrs.style.height = initialSize[1].toString() + 'px';
|
|
|
|
|
|
|
|
const watchers = new MszWatcherCollection;
|
|
|
|
watchers.define(MszVideoEmbedPlayerEvents());
|
|
|
|
|
|
|
|
const player = $e({
|
|
|
|
tag: 'video',
|
|
|
|
attrs: videoAttrs,
|
|
|
|
});
|
|
|
|
|
|
|
|
const setSize = function(w, h) {
|
|
|
|
const size = constrainSize(w, h, initialSize[0], initialSize[1]);
|
|
|
|
player.style.width = size[0].toString() + 'px';
|
|
|
|
player.style.height = size[1].toString() + 'px';
|
|
|
|
};
|
|
|
|
|
|
|
|
const pub = {
|
|
|
|
getElement: function() {
|
|
|
|
return player;
|
|
|
|
},
|
|
|
|
appendTo: function(target) {
|
|
|
|
target.appendChild(player);
|
|
|
|
},
|
|
|
|
insertBefore: function(ref) {
|
|
|
|
$ib(ref, player);
|
|
|
|
},
|
|
|
|
nuke: function() {
|
|
|
|
$r(player);
|
|
|
|
},
|
|
|
|
replaceElement(target) {
|
|
|
|
$ib(target, player);
|
|
|
|
$r(target);
|
|
|
|
},
|
|
|
|
getType: function() { return 'external'; },
|
|
|
|
getWidth: function() { return width; },
|
|
|
|
getHeight: function() { return height; },
|
|
|
|
};
|
|
|
|
|
2024-01-24 21:53:26 +00:00
|
|
|
pub.watch = (name, handler) => watchers.watch(name, handler);
|
|
|
|
pub.unwatch = (name, handler) => watchers.unwatch(name, handler);
|
2023-01-29 01:51:54 +00:00
|
|
|
|
|
|
|
if(shouldObserveResize)
|
|
|
|
player.addEventListener('resize', function() { setSize(player.videoWidth, player.videoHeight); });
|
|
|
|
|
2024-01-24 21:53:26 +00:00
|
|
|
player.addEventListener('play', function() { watchers.call('play'); });
|
2023-01-29 01:51:54 +00:00
|
|
|
|
|
|
|
const pPlay = function() { player.play(); };
|
|
|
|
pub.play = pPlay;
|
|
|
|
|
|
|
|
const pPause = function() { player.pause(); };
|
|
|
|
pub.pause = pPause;
|
|
|
|
|
|
|
|
let stopCalled = false;
|
|
|
|
player.addEventListener('pause', function() {
|
2024-01-24 21:53:26 +00:00
|
|
|
watchers.call(stopCalled ? 'stop' : 'pause');
|
2023-01-29 01:51:54 +00:00
|
|
|
stopCalled = false;
|
|
|
|
});
|
|
|
|
|
|
|
|
const pStop = function() {
|
|
|
|
stopCalled = true;
|
|
|
|
player.pause();
|
|
|
|
player.currentTime = 0;
|
|
|
|
};
|
|
|
|
pub.stop = pStop;
|
|
|
|
|
|
|
|
const pIsPlaying = function() { return !player.paused; };
|
|
|
|
pub.isPlaying = pIsPlaying;
|
|
|
|
|
|
|
|
const pIsMuted = function() { return player.muted; };
|
|
|
|
pub.isMuted = pIsMuted;
|
|
|
|
|
|
|
|
let lastMuteState = player.muted;
|
|
|
|
player.addEventListener('volumechange', function() {
|
|
|
|
if(lastMuteState !== player.muted) {
|
|
|
|
lastMuteState = player.muted;
|
2024-01-24 21:53:26 +00:00
|
|
|
watchers.call('mute', lastMuteState);
|
2023-01-29 01:51:54 +00:00
|
|
|
} else
|
2024-01-24 21:53:26 +00:00
|
|
|
watchers.call('volume', player.volume);
|
2023-01-29 01:51:54 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
const pSetMuted = function(state) { player.muted = state; };
|
|
|
|
pub.setMuted = pSetMuted;
|
|
|
|
|
|
|
|
const pGetVolume = function() { return player.volume; };
|
|
|
|
pub.getVolume = pGetVolume;
|
|
|
|
|
|
|
|
const pSetVolume = function(volume) { player.volume = volume; };
|
|
|
|
pub.setVolume = pSetVolume;
|
|
|
|
|
|
|
|
const pGetPlaybackRate = function() { return player.playbackRate; };
|
|
|
|
pub.getPlaybackRate = pGetPlaybackRate;
|
|
|
|
|
|
|
|
player.addEventListener('ratechange', function() {
|
2024-01-24 21:53:26 +00:00
|
|
|
watchers.call('rate', player.playbackRate);
|
2023-01-29 01:51:54 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
const pSetPlaybackRate = function(rate) { player.playbackRate = rate; };
|
|
|
|
pub.setPlaybackRate = pSetPlaybackRate;
|
|
|
|
|
|
|
|
window.addEventListener('durationchange', function() {
|
2024-01-24 21:53:26 +00:00
|
|
|
watchers.call('duration', player.duration);
|
2023-01-29 01:51:54 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
const pGetDuration = function() { return player.duration; };
|
|
|
|
pub.getDuration = pGetDuration;
|
|
|
|
|
|
|
|
window.addEventListener('timeupdate', function() {
|
2024-01-24 21:53:26 +00:00
|
|
|
watchers.call('time', player.currentTime);
|
2023-01-29 01:51:54 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
const pGetTime = function() { return player.currentTime; };
|
|
|
|
pub.getTime = pGetTime;
|
|
|
|
|
|
|
|
const pSeek = function(time) { player.currentTime = time; };
|
|
|
|
pub.seek = pSeek;
|
|
|
|
|
|
|
|
return pub;
|
|
|
|
};
|
|
|
|
|
|
|
|
const MszVideoEmbedYouTube = function(metadata, options) {
|
|
|
|
options = options || {};
|
|
|
|
|
|
|
|
const ytOrigin = 'https://www.youtube.com',
|
|
|
|
playerId = 'yt-' + MszUniqueStr(8),
|
|
|
|
shouldAutoplay = options.autoplay === undefined || options.autoplay;
|
|
|
|
|
|
|
|
let embedUrl = 'https://www.youtube.com/embed/' + metadata.youtube_video_id + '?enablejsapi=1';
|
|
|
|
|
|
|
|
embedUrl += '&origin=' + encodeURIComponent(location.origin);
|
|
|
|
|
|
|
|
if(metadata.youtube_start_time)
|
|
|
|
embedUrl += '&t=' + encodeURIComponent(metadata.youtube_start_time);
|
|
|
|
|
|
|
|
if(metadata.youtube_playlist) {
|
|
|
|
embedUrl += '&list=' + encodeURIComponent(metadata.youtube_playlist);
|
|
|
|
|
|
|
|
if(metadata.youtube_playlist_index)
|
|
|
|
embedUrl += '&index=' + encodeURIComponent(metadata.youtube_playlist_index);
|
|
|
|
}
|
|
|
|
|
|
|
|
let presetPlaybackRates = undefined,
|
|
|
|
isMuted = undefined,
|
|
|
|
volume = undefined,
|
|
|
|
playbackRate = undefined,
|
|
|
|
duration = undefined,
|
|
|
|
currentTime = undefined,
|
|
|
|
isPlaying = undefined;
|
|
|
|
|
|
|
|
const watchers = new MszWatcherCollection;
|
|
|
|
watchers.define(MszVideoEmbedPlayerEvents());
|
|
|
|
|
|
|
|
const player = $e({
|
|
|
|
tag: 'iframe',
|
|
|
|
attrs: {
|
|
|
|
frameborder: 0,
|
|
|
|
allow: 'accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture',
|
|
|
|
allowfullscreen: 'allowfullscreen',
|
|
|
|
src: embedUrl,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
const pub = {
|
|
|
|
getElement: function() {
|
|
|
|
return player;
|
|
|
|
},
|
|
|
|
appendTo: function(target) {
|
|
|
|
target.appendChild(player);
|
|
|
|
},
|
|
|
|
insertBefore: function(ref) {
|
|
|
|
$ib(ref, player);
|
|
|
|
},
|
|
|
|
nuke: function() {
|
|
|
|
$r(player);
|
|
|
|
},
|
|
|
|
replaceElement(target) {
|
|
|
|
$ib(target, player);
|
|
|
|
$r(target);
|
|
|
|
},
|
|
|
|
getType: function() { return 'youtube'; },
|
|
|
|
getWidth: function() { return 560; },
|
|
|
|
getHeight: function() { return 315; },
|
|
|
|
getPlayerId: function() { return playerId; },
|
|
|
|
};
|
|
|
|
|
2024-01-24 21:53:26 +00:00
|
|
|
pub.watch = (name, handler) => watchers.watch(name, handler);
|
|
|
|
pub.unwatch = (name, handler) => watchers.unwatch(name, handler);
|
2023-01-29 01:51:54 +00:00
|
|
|
|
|
|
|
const postMessage = function(data) {
|
|
|
|
player.contentWindow.postMessage(JSON.stringify(data), ytOrigin);
|
|
|
|
};
|
|
|
|
const postCommand = function(name, args) {
|
|
|
|
postMessage({
|
|
|
|
id: playerId,
|
|
|
|
event: 'command',
|
|
|
|
func: name,
|
|
|
|
args: args || [],
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
const cmdPlay = function() { postCommand('playVideo'); };
|
|
|
|
const cmdPause = function() { postCommand('pauseVideo'); };
|
|
|
|
const cmdStop = function() { postCommand('stopVideo'); };
|
|
|
|
const cmdMute = function() { postCommand('mute'); };
|
|
|
|
const cmdUnMute = function() { postCommand('unMute'); };
|
|
|
|
const cmdSetVolume = function(volume) { postCommand('setVolume', [volume * 100]); };
|
|
|
|
const cmdSetPlaybackRate = function(rate) { postCommand('setPlaybackRate', [rate]); };
|
|
|
|
const cmdSeekTo = function(timecode) { postCommand('seekTo', [timecode, true]); };
|
|
|
|
|
|
|
|
const cmdMuteState = function(state) {
|
|
|
|
if(state) cmdMute();
|
|
|
|
else cmdUnMute();
|
|
|
|
};
|
|
|
|
|
|
|
|
const varIsPlaying = function() { return isPlaying; };
|
|
|
|
const varIsMuted = function() { return isMuted; };
|
|
|
|
const varVolume = function() { return volume; };
|
|
|
|
const varRate = function() { return playbackRate; };
|
|
|
|
const varDuration = function() { return duration; };
|
|
|
|
const varTime = function() { return currentTime; };
|
|
|
|
|
|
|
|
let lastPlayerState = undefined,
|
|
|
|
lastPlayerStateEvent = undefined;
|
|
|
|
const handlePlayerState = function(state) {
|
|
|
|
let eventName = undefined;
|
|
|
|
if(state === 1) {
|
|
|
|
isPlaying = true;
|
|
|
|
eventName = 'play';
|
|
|
|
} else {
|
|
|
|
isPlaying = false;
|
|
|
|
if(state === 2)
|
|
|
|
eventName = 'pause';
|
|
|
|
else if(lastPlayerState === -1 && state === 5)
|
|
|
|
eventName = 'stop';
|
|
|
|
}
|
|
|
|
|
|
|
|
lastPlayerState = state;
|
|
|
|
if(eventName !== undefined && eventName !== lastPlayerStateEvent) {
|
|
|
|
lastPlayerStateEvent = eventName;
|
2024-01-24 21:53:26 +00:00
|
|
|
watchers.call(eventName);
|
2023-01-29 01:51:54 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleMuted = function(muted) {
|
|
|
|
isMuted = muted;
|
2024-01-24 21:53:26 +00:00
|
|
|
watchers.call('mute', isMuted);
|
2023-01-29 01:51:54 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const handleVolume = function(value) {
|
|
|
|
volume = value / 100;
|
2024-01-24 21:53:26 +00:00
|
|
|
watchers.call('volume', volume);
|
2023-01-29 01:51:54 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const handleRate = function(rate) {
|
|
|
|
playbackRate = rate;
|
2024-01-24 21:53:26 +00:00
|
|
|
watchers.call('rate', playbackRate);
|
2023-01-29 01:51:54 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const handleDuration = function(time) {
|
|
|
|
duration = time;
|
2024-01-24 21:53:26 +00:00
|
|
|
watchers.call('duration', duration);
|
2023-01-29 01:51:54 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const handleTime = function(time) {
|
|
|
|
currentTime = time;
|
2024-01-24 21:53:26 +00:00
|
|
|
watchers.call('time', currentTime);
|
2023-01-29 01:51:54 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const handlePresetRates = function(rates) {
|
|
|
|
presetPlaybackRates = rates;
|
|
|
|
};
|
|
|
|
|
|
|
|
const infoHandlers = {
|
|
|
|
'playerState': handlePlayerState,
|
|
|
|
'muted': handleMuted,
|
|
|
|
'volume': handleVolume,
|
|
|
|
'playbackRate': handleRate,
|
|
|
|
'duration': handleDuration,
|
|
|
|
'currentTime': handleTime,
|
|
|
|
'availablePlaybackRates': handlePresetRates,
|
|
|
|
};
|
|
|
|
|
|
|
|
const processInfo = function(info) {
|
|
|
|
for(const name in infoHandlers)
|
|
|
|
if(name in info && info[name] !== undefined)
|
|
|
|
infoHandlers[name](info[name]);
|
|
|
|
};
|
|
|
|
|
|
|
|
window.addEventListener('message', function(ev) {
|
|
|
|
if(ev.origin !== ytOrigin || typeof ev.data !== 'string')
|
|
|
|
return;
|
|
|
|
|
|
|
|
const data = JSON.parse(ev.data);
|
|
|
|
if(!data || data.id !== playerId)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if(data.event === 'initialDelivery') {
|
|
|
|
if(data.info !== undefined)
|
|
|
|
processInfo(data.info);
|
|
|
|
} else if(data.event === 'onReady') {
|
|
|
|
if(shouldAutoplay)
|
|
|
|
cmdPlay();
|
|
|
|
} else if(data.event === 'infoDelivery') {
|
|
|
|
if(data.info !== undefined)
|
|
|
|
processInfo(data.info);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
player.addEventListener('load', function(ev) {
|
|
|
|
postMessage({
|
|
|
|
id: playerId,
|
|
|
|
event: 'listening',
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
pub.play = cmdPlay;
|
|
|
|
pub.pause = cmdPause;
|
|
|
|
pub.stop = cmdStop;
|
|
|
|
pub.isPlaying = varIsPlaying;
|
|
|
|
pub.isMuted = varIsMuted;
|
|
|
|
pub.setMuted = cmdMuteState;
|
|
|
|
pub.getVolume = varVolume;
|
|
|
|
pub.setVolume = cmdSetVolume;
|
|
|
|
pub.getRate = varRate;
|
|
|
|
pub.setRate = cmdSetPlaybackRate;
|
|
|
|
pub.getDuration = varDuration;
|
|
|
|
pub.getTime = varTime;
|
|
|
|
pub.seek = cmdSeekTo;
|
|
|
|
|
|
|
|
return pub;
|
|
|
|
};
|
|
|
|
|
|
|
|
const MszVideoEmbedNicoNico = function(metadata, options) {
|
|
|
|
options = options || {};
|
|
|
|
|
|
|
|
const nndOrigin = 'https://embed.nicovideo.jp',
|
|
|
|
playerId = 'nnd-' + MszUniqueStr(8),
|
|
|
|
shouldAutoplay = options.autoplay === undefined || options.autoplay;
|
|
|
|
|
|
|
|
let embedUrl = 'https://embed.nicovideo.jp/watch/' + metadata.nicovideo_video_id + '?jsapi=1&playerId=' + playerId;
|
|
|
|
|
|
|
|
if(metadata.nicovideo_start_time)
|
|
|
|
embedUrl += '&from=' + encodeURIComponent(metadata.nicovideo_start_time);
|
|
|
|
|
|
|
|
let isMuted = undefined,
|
|
|
|
volume = undefined,
|
|
|
|
duration = undefined,
|
|
|
|
currentTime = undefined,
|
|
|
|
isPlaying = false;
|
|
|
|
|
|
|
|
const watchers = new MszWatcherCollection;
|
|
|
|
watchers.define(MszVideoEmbedPlayerEvents());
|
|
|
|
|
|
|
|
const player = $e({
|
|
|
|
tag: 'iframe',
|
|
|
|
attrs: {
|
|
|
|
frameborder: 0,
|
|
|
|
allow: 'autoplay',
|
|
|
|
allowfullscreen: 'allowfullscreen',
|
|
|
|
src: embedUrl,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
const pub = {
|
|
|
|
getElement: function() {
|
|
|
|
return player;
|
|
|
|
},
|
|
|
|
appendTo: function(target) {
|
|
|
|
target.appendChild(player);
|
|
|
|
},
|
|
|
|
insertBefore: function(ref) {
|
|
|
|
$ib(ref, player);
|
|
|
|
},
|
|
|
|
nuke: function() {
|
|
|
|
$r(player);
|
|
|
|
},
|
|
|
|
replaceElement(target) {
|
|
|
|
$ib(target, player);
|
|
|
|
$r(target);
|
|
|
|
},
|
|
|
|
getType: function() { return 'nicovideo'; },
|
|
|
|
getWidth: function() { return 640; },
|
|
|
|
getHeight: function() { return 360; },
|
|
|
|
getPlayerId: function() { return playerId; },
|
|
|
|
};
|
|
|
|
|
2024-01-24 21:53:26 +00:00
|
|
|
pub.watch = (name, handler) => watchers.watch(name, handler);
|
|
|
|
pub.unwatch = (name, handler) => watchers.unwatch(name, handler);
|
2023-01-29 01:51:54 +00:00
|
|
|
|
|
|
|
const postMessage = function(name, data) {
|
|
|
|
if(name === undefined)
|
|
|
|
throw 'name must be specified';
|
|
|
|
|
|
|
|
player.contentWindow.postMessage({
|
|
|
|
playerId: playerId,
|
|
|
|
sourceConnectorType: 1,
|
|
|
|
eventName: name,
|
|
|
|
data: data,
|
|
|
|
}, nndOrigin);
|
|
|
|
};
|
|
|
|
|
|
|
|
const cmdPlay = function() { postMessage('play'); };
|
|
|
|
const cmdPause = function() { postMessage('pause'); };
|
|
|
|
const cmdMute = function(state) { postMessage('mute', { mute: state }); };
|
|
|
|
const cmdSeek = function(time) { postMessage('seek', { time: time }); };
|
|
|
|
const cmdVolumeChange = function(volume) { postMessage('volumeChange', { volume: volume }); };
|
|
|
|
|
|
|
|
let stopCalled = false;
|
|
|
|
const cmdStop = function() {
|
|
|
|
stopCalled = true;
|
|
|
|
cmdPause();
|
|
|
|
cmdSeek(0);
|
|
|
|
};
|
|
|
|
|
|
|
|
const varIsPlaying = function() { return isPlaying; };
|
|
|
|
const varIsMuted = function() { return isMuted; };
|
|
|
|
const varVolume = function() { return volume; };
|
|
|
|
const varDuration = function() { return duration; };
|
|
|
|
const varTime = function() { return currentTime; };
|
|
|
|
|
|
|
|
let lastPlayerStateEvent = undefined;
|
|
|
|
const handlePlayerStatus = function(status) {
|
|
|
|
let eventName = undefined;
|
|
|
|
if(status === 2) {
|
|
|
|
isPlaying = true;
|
|
|
|
eventName = 'play';
|
|
|
|
} else {
|
|
|
|
isPlaying = false;
|
|
|
|
if(status === 4 || stopCalled) {
|
|
|
|
eventName = 'stop';
|
|
|
|
stopCalled = false;
|
|
|
|
} else if(status === 3)
|
|
|
|
eventName = 'pause';
|
|
|
|
}
|
|
|
|
|
|
|
|
if(eventName !== undefined && eventName !== lastPlayerStateEvent) {
|
|
|
|
lastPlayerStateEvent = eventName;
|
2024-01-24 21:53:26 +00:00
|
|
|
watchers.call(eventName);
|
2023-01-29 01:51:54 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleMuted = function(muted) {
|
|
|
|
isMuted = muted;
|
2024-01-24 21:53:26 +00:00
|
|
|
watchers.call('mute', isMuted);
|
2023-01-29 01:51:54 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const handleVolume = function(value) {
|
|
|
|
volume = value;
|
2024-01-24 21:53:26 +00:00
|
|
|
watchers.call('volume', volume);
|
2023-01-29 01:51:54 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const handleDuration = function(time) {
|
|
|
|
duration = time / 1000;
|
2024-01-24 21:53:26 +00:00
|
|
|
watchers.call('duration', duration);
|
2023-01-29 01:51:54 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const handleTime = function(time) {
|
|
|
|
currentTime = time / 1000;
|
2024-01-24 21:53:26 +00:00
|
|
|
watchers.call('time', currentTime);
|
2023-01-29 01:51:54 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const metadataHanders = {
|
|
|
|
'muted': handleMuted,
|
|
|
|
'volume': handleVolume,
|
|
|
|
'duration': handleDuration,
|
|
|
|
'currentTime': handleTime,
|
|
|
|
};
|
|
|
|
const statusHandlers = {
|
|
|
|
'playerStatus': handlePlayerStatus,
|
|
|
|
};
|
|
|
|
|
|
|
|
const processData = function(handlers, info) {
|
|
|
|
for(const name in handlers)
|
|
|
|
if(name in info && info[name] !== undefined)
|
|
|
|
handlers[name](info[name]);
|
|
|
|
};
|
|
|
|
|
|
|
|
window.addEventListener('message', function(ev) {
|
|
|
|
if(ev.origin !== nndOrigin || ev.data.playerId !== playerId)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if(ev.data.eventName === 'loadComplete') {
|
|
|
|
if(shouldAutoplay)
|
|
|
|
cmdPlay();
|
|
|
|
} else if(ev.data.eventName === 'playerMetadataChange') {
|
|
|
|
if(ev.data.data !== undefined)
|
|
|
|
processData(metadataHanders, ev.data.data);
|
|
|
|
} else if(ev.data.eventName === 'statusChange') {
|
|
|
|
if(ev.data.data !== undefined)
|
|
|
|
processData(statusHandlers, ev.data.data);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
pub.play = cmdPlay;
|
|
|
|
pub.pause = cmdPause;
|
|
|
|
pub.stop = cmdStop;
|
|
|
|
pub.isPlaying = varIsPlaying;
|
|
|
|
pub.isMuted = varIsMuted;
|
|
|
|
pub.setMuted = cmdMute;
|
|
|
|
pub.getVolume = varVolume;
|
|
|
|
pub.setVolume = cmdVolumeChange;
|
|
|
|
pub.getDuration = varDuration;
|
|
|
|
pub.getTime = varTime;
|
|
|
|
pub.seek = cmdSeek;
|
|
|
|
|
|
|
|
return pub;
|
|
|
|
};
|
|
|
|
|
|
|
|
const MszVideoEmbedPlaceholder = function(metadata, options) {
|
|
|
|
options = options || {};
|
|
|
|
|
|
|
|
if(typeof options.player !== 'function' && typeof options.onclick !== 'function')
|
|
|
|
throw 'Neither a player nor an onclick handler were provided.';
|
|
|
|
|
|
|
|
const shouldAutoSize = options.autosize === undefined || options.autosize;
|
|
|
|
|
|
|
|
const infoChildren = [];
|
|
|
|
|
|
|
|
infoChildren.push({
|
|
|
|
tag: 'h1',
|
|
|
|
attrs: {
|
|
|
|
className: 'embedph-info-title',
|
|
|
|
},
|
|
|
|
child: metadata.title,
|
|
|
|
});
|
|
|
|
|
|
|
|
if(metadata.description) {
|
2023-01-29 03:35:45 +00:00
|
|
|
let firstLine = metadata.description.split("\n")[0].trim();
|
2023-01-29 01:51:54 +00:00
|
|
|
if(firstLine.length > 300)
|
|
|
|
firstLine = firstLine.substring(0, 300).trim() + '...';
|
|
|
|
|
|
|
|
infoChildren.push({
|
|
|
|
tag: 'div',
|
|
|
|
attrs: {
|
|
|
|
className: 'embedph-info-desc',
|
|
|
|
},
|
|
|
|
child: firstLine,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
infoChildren.push({
|
|
|
|
tag: 'div',
|
|
|
|
attrs: {
|
|
|
|
className: 'embedph-info-site',
|
|
|
|
},
|
|
|
|
child: metadata.site_name,
|
|
|
|
});
|
|
|
|
|
|
|
|
const style = [];
|
|
|
|
if(typeof metadata.color !== 'undefined')
|
|
|
|
style.push('--embedph-colour: ' + metadata.color);
|
|
|
|
|
|
|
|
if(!shouldAutoSize) {
|
|
|
|
const size = MszVideoConstrainSize(
|
|
|
|
options.width || metadata.width || 200,
|
|
|
|
options.height || metadata.height || 200,
|
|
|
|
options.maxWidth,
|
|
|
|
options.maxHeight
|
|
|
|
);
|
|
|
|
|
|
|
|
style.push('width: ' + size[0].toString() + 'px');
|
|
|
|
style.push('height: ' + size[1].toString() + 'px');
|
|
|
|
}
|
|
|
|
|
|
|
|
const pub = {};
|
|
|
|
|
|
|
|
const elem = $e({
|
|
|
|
attrs: {
|
|
|
|
className: ('embedph embedph-' + (options.type || 'external')),
|
|
|
|
style: style.join(';'),
|
|
|
|
},
|
|
|
|
child: [
|
|
|
|
{
|
|
|
|
attrs: {
|
|
|
|
className: 'embedph-bg',
|
|
|
|
},
|
|
|
|
child: {
|
|
|
|
tag: 'img',
|
|
|
|
attrs: {
|
|
|
|
src: metadata.image,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
attrs: {
|
|
|
|
className: 'embedph-fg',
|
|
|
|
},
|
|
|
|
child: [
|
|
|
|
{
|
|
|
|
attrs: {
|
|
|
|
className: 'embedph-info',
|
|
|
|
},
|
|
|
|
child: {
|
|
|
|
attrs: {
|
|
|
|
className: 'embedph-info-wrap',
|
|
|
|
},
|
|
|
|
child: [
|
|
|
|
{
|
|
|
|
attrs: {
|
|
|
|
className: 'embedph-info-bar',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
attrs: {
|
|
|
|
className: 'embedph-info-body',
|
|
|
|
},
|
|
|
|
child: infoChildren,
|
|
|
|
}
|
|
|
|
],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
attrs: {
|
|
|
|
className: 'embedph-play',
|
|
|
|
onclick: function(ev) {
|
|
|
|
if(ev.target.tagName.toLowerCase() === 'a')
|
|
|
|
return;
|
|
|
|
|
|
|
|
if(typeof options.onclick === 'function') {
|
|
|
|
options.onclick(ev);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const player = new options.player(metadata, options);
|
|
|
|
let frameOrPlayer = player;
|
|
|
|
|
|
|
|
if(typeof options.frame === 'function')
|
|
|
|
frameOrPlayer = new options.frame(player, options);
|
|
|
|
|
|
|
|
const embed = new MszVideoEmbed(frameOrPlayer);
|
|
|
|
if(options.autoembed === undefined || options.autoembed)
|
|
|
|
embed.replaceElement(elem);
|
|
|
|
|
|
|
|
if(typeof options.onembed === 'function')
|
|
|
|
options.onembed(embed);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
child: [
|
|
|
|
{
|
|
|
|
attrs: {
|
|
|
|
className: 'embedph-play-internal',
|
|
|
|
},
|
|
|
|
child: {
|
|
|
|
tag: 'i',
|
|
|
|
attrs: {
|
|
|
|
className: 'fas fa-play fa-4x fa-fw',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
tag: 'a',
|
|
|
|
attrs: {
|
|
|
|
className: 'embedph-play-external',
|
|
|
|
href: metadata.url,
|
|
|
|
target: '_blank',
|
|
|
|
rel: 'noopener',
|
|
|
|
},
|
|
|
|
child: ('or watch on ' + metadata.site_name + '?'),
|
|
|
|
}
|
|
|
|
],
|
2023-01-29 20:29:20 +00:00
|
|
|
},
|
2023-01-29 01:51:54 +00:00
|
|
|
],
|
|
|
|
},
|
|
|
|
],
|
|
|
|
});
|
|
|
|
|
|
|
|
pub.getElement = function() { return elem; };
|
|
|
|
pub.appendTo = function(target) { target.appendChild(elem); };
|
|
|
|
pub.insertBefore = function(ref) { $ib(ref, elem); };
|
|
|
|
pub.nuke = function() {
|
|
|
|
$r(elem);
|
|
|
|
};
|
|
|
|
pub.replaceElement = function(target) {
|
|
|
|
$ib(target, elem);
|
|
|
|
$r(target);
|
|
|
|
};
|
|
|
|
|
|
|
|
return pub;
|
|
|
|
};
|