f0ckv2/public/s/js/v0ck.js
2021-05-15 13:58:24 +02:00

176 lines
7.5 KiB
JavaScript

const tpl_player = svg => `<div class="v0ck_player_controls">
<div class="v0ck_progress">
<div class="v0ck_progress_filled"></div>
</div>
<button class="v0ck_player_button v0ck_tplay v0ck_toggle" title="Play">
<svg style="width: 20px; height: 20px;">
<use id="v0ck_svg_play" href="${svg}#play"></use>
<use id="v0ck_svg_pause" class="v0ck_hidden" href="${svg}#pause"></use>
</svg>
</button>
<button class="v0ck_player_button v0ck_volume">
<svg style="width: 20px; height: 20px;">
<use id="v0ck_svg_volume_full" href="${svg}#volume_full"></use>
<use id="v0ck_svg_volume_mid" class="v0ck_hidden" href="${svg}#volume_mid"></use>
<use id="v0ck_svg_volume_mute" class="v0ck_hidden" href="${svg}#volume_mute"></use>
</svg>
</button>
<input type="range" name="volume" min="0" max="1" step="0.01" value="1" />
<button class="v0ck_player_button v0ck_playtime">00:00 / 00:00</button>
<span style="flex: 30"></span>
<button data-skip="-10" class="v0ck_player_button">
<svg style="width: 20px; height: 20px;"><use id="v0ck_svg_backward" href="${svg}#backward"></use></svg>
</button>
<button data-skip="10" class="v0ck_player_button">
<svg style="width: 20px; height: 20px;"><use id="v0ck_svg_forward" href="${svg}#forward"></use></svg>
</button>
<button class="v0ck_player_button v0ck_toggle v0ck_fullscreen" title="Full Screen">
<svg style="width: 20px; height: 20px;"><use id="v0ck_svg_fullscreen" href="${svg}#fullscreen"></use></svg>
</button>
</div>
<div class="v0ck_overlay v0ck_hidden">
<svg><use id="v0ck_svg_ol_play" href="${svg}#ol_play"></use></svg>
</div>`;
class v0ck {
constructor(elem) {
const tagName = elem.tagName.toLowerCase();
if(["video", "audio"].includes(tagName)) {
const parent = elem.parentElement;
parent.classList.add("v0ck", "paused");
elem.classList.add("v0ck_video", "viewer");
document.head.insertAdjacentHTML("beforeend", `<link rel="stylesheet" href="/s/css/v0ck.css">`); // inject css
elem.insertAdjacentHTML("afterend", tpl_player("/s/img/v0ck.svg"));
if(tagName === "audio" && elem.hasAttribute('poster')) { // set cover
const player = document.querySelector('.v0ck');
player.style.backgroundImage = `url('${elem.getAttribute('poster')}')`;
}
}
else
return console.error("nope");
return this.init(elem);
}
init(elem) {
const player = document.querySelector('.v0ck');
const video = elem;
video.removeAttribute('autoplay');
video.removeAttribute('controls');
const progress = player.querySelector('.v0ck_progress');
const progressBar = player.querySelector('.v0ck_progress_filled');
const toggle = player.querySelector('.v0ck_toggle');
const skipButtons = player.querySelectorAll('.v0ck [data-skip]');
const ranges = player.querySelectorAll('.v0ck input[type="range"]');
const volumeSlider = player.querySelector('.v0ck input[type="range"][name="volume"]');
const fullscreen = player.querySelector('.v0ck_fullscreen');
const playtime = player.querySelector('.v0ck_playtime');
const overlay = player.querySelector('.v0ck_overlay');
const volumeButton = player.querySelector('.v0ck_volume');
const volumeSymbols = volumeButton.querySelectorAll('.v0ck use');
const defaultVolume = 0.5;
let mousedown = false;
let _volume;
function handleVolumeButton(vol) {
[...volumeSymbols].forEach(s => !s.classList.contains('v0ck_hidden') ? s.classList.add('v0ck_hidden') : null);
switch(true) {
case(vol === 0): [...volumeSymbols].filter(s => s.id === "v0ck_svg_volume_mute")[0].classList.toggle('v0ck_hidden'); break;
case(vol <= 0.5 && vol > 0): [...volumeSymbols].filter(s => s.id === "v0ck_svg_volume_mid")[0].classList.toggle('v0ck_hidden'); break;
case(vol > 0.5): [...volumeSymbols].filter(s => s.id === "v0ck_svg_volume_full")[0].classList.toggle('v0ck_hidden'); break;
}
localStorage.setItem("volume", vol);
}
function togglePlay() {
const status = video[video.paused ? 'play' : 'pause']();
if(status !== undefined) { // todo: merge with updatePlayIcon
status
.then(_ => {
overlay.classList[video.paused ? 'remove' : 'add']('v0ck_hidden');
})
.catch(err => {
overlay.classList.toggle('v0ck_hidden');
});
}
else
overlay.classList[video.paused ? 'remove' : 'add']('v0ck_hidden');
}
function updatePlayIcon() {
toggle.classList.toggle('playing');
player.classList.toggle('paused');
toggle.setAttribute('title', toggle.classList.contains('playing') ? 'Pause' : 'Play');
[...toggle.querySelectorAll('use')].forEach(icon => icon.classList.toggle('v0ck_hidden'));
}
function toggleMute(e) {
if(video.volume === 0)
video.volume = volumeSlider.value = _volume === 0 ? defaultVolume : _volume;
else {
_volume = video.volume;
video.volume = volumeSlider.value = 0;
}
handleVolumeButton(video.volume);
}
function skip() {
video.currentTime += +this.dataset.skip;
}
function handleRangeUpdate() {
video[this.name] = this.value;
_volume = video.volume;
handleVolumeButton(video.volume);
}
function formatTime(seconds) {
const minutes = (~~(seconds / 60)).toString().padStart(2, "0");
seconds = (~~(seconds % 60)).toString().padStart(2, "0");
return minutes + ":" + seconds;
}
function handleProgress() {
const percent = (video.currentTime / video.duration) * 100;
progressBar.style.flexBasis = percent + '%';
playtime.innerText = `${formatTime(video.currentTime)} / ${formatTime(video.duration)}`;
}
function scrub(e) {
video.currentTime = (e.offsetX / progress.offsetWidth) * video.duration;
}
function toggleFullScreen(e) {
if(document.fullscreenElement) // exit fullscreen
document.exitFullscreen();
else { // request fullscreen
if(/(iPad|iPhone|iPod)/gi.test(navigator.platform))
video.webkitEnterFullscreen();
else
player.requestFullscreen();
}
}
function toggleFullScreenClasses() {
player.classList.toggle('fullscreen');
}
player.addEventListener('click', e => {
const path = e.path || (e.composedPath && e.composedPath());
if(!path.filter(f => f.classList?.contains('v0ck_player_controls')).length)
togglePlay(e);
});
toggle.addEventListener('click', togglePlay);
video.addEventListener('play', updatePlayIcon);
video.addEventListener('pause', updatePlayIcon);
video.addEventListener('timeupdate', handleProgress);
volumeButton.addEventListener('click', toggleMute);
skipButtons.forEach(button => button.addEventListener('click', skip));
ranges.forEach(range => range.addEventListener('change', handleRangeUpdate));
ranges.forEach(range => range.addEventListener('mousemove', handleRangeUpdate));
progress.addEventListener('click', scrub);
progress.addEventListener('mousemove', e => mousedown && scrub(e));
progress.addEventListener('mousedown', () => mousedown = true);
progress.addEventListener('mouseup', () => mousedown = false);
fullscreen.addEventListener('click', toggleFullScreen);
document.addEventListener('fullscreenchange', toggleFullScreenClasses);
video.volume = _volume = volumeSlider.value = +(localStorage.getItem('volume') ?? defaultVolume);
handleVolumeButton(video.volume);
togglePlay();
return video;
}
}