168 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			168 lines
		
	
	
		
			7.0 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 id="togglebg">💡</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('controls');
 | 
						|
    video.removeAttribute('autoplay');
 | 
						|
    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('mousedown', scrub);
 | 
						|
    fullscreen.addEventListener('click', toggleFullScreen);
 | 
						|
    document.addEventListener('fullscreenchange', toggleFullScreenClasses);
 | 
						|
 | 
						|
    video.volume = _volume = volumeSlider.value = +(localStorage.getItem('volume') ?? defaultVolume);
 | 
						|
    handleVolumeButton(video.volume);
 | 
						|
    togglePlay();
 | 
						|
 | 
						|
    return video;
 | 
						|
  }
 | 
						|
}
 |