const tpl_player = svg => `
`; 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", ``); // 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; } }