Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Press Ctrl-F5.
/* Any JavaScript here will be loaded for all users on every page load. */ mw.hook('wikipage.content').add(function($content) { var $bandcamp = $content.find( '.bandcamp:not(.loaded)' ); if ( !$bandcamp.length ) return; $bandcamp.each( function() { var elem = $( this ); var width = elem.attr( 'data-width' ), height = elem.attr( 'data-height' ), data_src = elem.attr( 'data-src' ); if ( !/^https?:\/\/bandcamp\.com\//.test( data_src ) ) return; elem.empty(); var is_px = [ true, true ]; // width, height if ( /%/.test( width ) || !/\d+/.test( width ) ) is_px[ 0 ] = false; if ( /%/.test( height ) ) is_px[ 1 ] = false; var frame_width = parseFloat( width, 10 ) || 100; frame_height = height ? ( parseFloat( height, 10 ) || 'auto' ) : ''; $( '<iframe />', { style: 'border: 0', width: frame_width + ( is_px[ 0 ] ? 'px' : '%' ), height: frame_height + ( frame_height ? ( is_px[ 1 ] ? 'px': '%' ) : '' ), src: data_src }).appendTo( elem ); elem.addClass( 'loaded' ); }); }); import lottieWeb from 'https://cdn.skypack.dev/lottie-web'; class AudioPlayer extends HTMLElement { constructor() { super(); const template = document.querySelector('template'); const templateContent = template.content; const shadow = this.attachShadow({mode: 'open'}); shadow.appendChild(templateContent.cloneNode(true)); } connectedCallback() { everything(this); } } const everything = function(element) { const shadow = element.shadowRoot; const audioPlayerContainer = shadow.getElementById('audio-player-container'); const playIconContainer = shadow.getElementById('play-icon'); const seekSlider = shadow.getElementById('seek-slider'); const volumeSlider = shadow.getElementById('volume-slider'); const muteIconContainer = shadow.getElementById('mute-icon'); const audio = shadow.querySelector('audio'); const durationContainer = shadow.getElementById('duration'); const currentTimeContainer = shadow.getElementById('current-time'); const outputContainer = shadow.getElementById('volume-output'); let playState = 'play'; let muteState = 'unmute'; let raf = null; audio.src = element.getAttribute('data-src'); const playAnimation = lottieWeb.loadAnimation({ container: playIconContainer, path: 'https://maxst.icons8.com/vue-static/landings/animated-icons/icons/pause/pause.json', renderer: 'svg', loop: false, autoplay: false, name: "Play Animation", }); const muteAnimation = lottieWeb.loadAnimation({ container: muteIconContainer, path: 'https://maxst.icons8.com/vue-static/landings/animated-icons/icons/mute/mute.json', renderer: 'svg', loop: false, autoplay: false, name: "Mute Animation", }); playAnimation.goToAndStop(14, true); const whilePlaying = () => { seekSlider.value = Math.floor(audio.currentTime); currentTimeContainer.textContent = calculateTime(seekSlider.value); audioPlayerContainer.style.setProperty('--seek-before-width', `${seekSlider.value / seekSlider.max * 100}%`); raf = requestAnimationFrame(whilePlaying); } const showRangeProgress = (rangeInput) => { if(rangeInput === seekSlider) audioPlayerContainer.style.setProperty('--seek-before-width', rangeInput.value / rangeInput.max * 100 + '%'); else audioPlayerContainer.style.setProperty('--volume-before-width', rangeInput.value / rangeInput.max * 100 + '%'); } const calculateTime = (secs) => { const minutes = Math.floor(secs / 60); const seconds = Math.floor(secs % 60); const returnedSeconds = seconds < 10 ? `0${seconds}` : `${seconds}`; return `${minutes}:${returnedSeconds}`; } const displayDuration = () => { durationContainer.textContent = calculateTime(audio.duration); } const setSliderMax = () => { seekSlider.max = Math.floor(audio.duration); } const displayBufferedAmount = () => { const bufferedAmount = Math.floor(audio.buffered.end(audio.buffered.length - 1)); audioPlayerContainer.style.setProperty('--buffered-width', `${(bufferedAmount / seekSlider.max) * 100}%`); } if (audio.readyState > 0) { displayDuration(); setSliderMax(); displayBufferedAmount(); } else { audio.addEventListener('loadedmetadata', () => { displayDuration(); setSliderMax(); displayBufferedAmount(); }); } playIconContainer.addEventListener('click', () => { if(playState === 'play') { audio.play(); playAnimation.playSegments([14, 27], true); requestAnimationFrame(whilePlaying); playState = 'pause'; } else { audio.pause(); playAnimation.playSegments([0, 14], true); cancelAnimationFrame(raf); playState = 'play'; } }); muteIconContainer.addEventListener('click', () => { if(muteState === 'unmute') { muteAnimation.playSegments([0, 15], true); audio.muted = true; muteState = 'mute'; } else { muteAnimation.playSegments([15, 25], true); audio.muted = false; muteState = 'unmute'; } }); audio.addEventListener('progress', displayBufferedAmount); seekSlider.addEventListener('input', (e) => { showRangeProgress(e.target); currentTimeContainer.textContent = calculateTime(seekSlider.value); if(!audio.paused) { cancelAnimationFrame(raf); } }); seekSlider.addEventListener('change', () => { audio.currentTime = seekSlider.value; if(!audio.paused) { requestAnimationFrame(whilePlaying); } }); volumeSlider.addEventListener('input', (e) => { const value = e.target.value; showRangeProgress(e.target); outputContainer.textContent = value; audio.volume = value / 100; }); if('mediaSession' in navigator) { navigator.mediaSession.metadata = new MediaMetadata({ title: 'Komorebi', artist: 'Anitek', album: 'MainStay', artwork: [ { src: 'https://assets.codepen.io/4358584/1.300.jpg', sizes: '96x96', type: 'image/png' }, { src: 'https://assets.codepen.io/4358584/1.300.jpg', sizes: '128x128', type: 'image/png' }, { src: 'https://assets.codepen.io/4358584/1.300.jpg', sizes: '192x192', type: 'image/png' }, { src: 'https://assets.codepen.io/4358584/1.300.jpg', sizes: '256x256', type: 'image/png' }, { src: 'https://assets.codepen.io/4358584/1.300.jpg', sizes: '384x384', type: 'image/png' }, { src: 'https://assets.codepen.io/4358584/1.300.jpg', sizes: '512x512', type: 'image/png' } ] }); navigator.mediaSession.setActionHandler('play', () => { if(playState === 'play') { audio.play(); playAnimation.playSegments([14, 27], true); requestAnimationFrame(whilePlaying); playState = 'pause'; } else { audio.pause(); playAnimation.playSegments([0, 14], true); cancelAnimationFrame(raf); playState = 'play'; } }); navigator.mediaSession.setActionHandler('pause', () => { if(playState === 'play') { audio.play(); playAnimation.playSegments([14, 27], true); requestAnimationFrame(whilePlaying); playState = 'pause'; } else { audio.pause(); playAnimation.playSegments([0, 14], true); cancelAnimationFrame(raf); playState = 'play'; } }); navigator.mediaSession.setActionHandler('seekbackward', (details) => { audio.currentTime = audio.currentTime - (details.seekOffset || 10); }); navigator.mediaSession.setActionHandler('seekforward', (details) => { audio.currentTime = audio.currentTime + (details.seekOffset || 10); }); navigator.mediaSession.setActionHandler('seekto', (details) => { if (details.fastSeek && 'fastSeek' in audio) { audio.fastSeek(details.seekTime); return; } audio.currentTime = details.seekTime; }); navigator.mediaSession.setActionHandler('stop', () => { audio.currentTime = 0; seekSlider.value = 0; audioPlayerContainer.style.setProperty('--seek-before-width', '0%'); currentTimeContainer.textContent = '0:00'; if(playState === 'pause') { playAnimation.playSegments([0, 14], true); cancelAnimationFrame(raf); playState = 'play'; } }); } } customElements.define('audio-player', AudioPlayer); /* GreenAudioPlayer.init({*/ /* selector: '.player', // inits Green Audio Player on each audio container that has class "player"*/ /* stopOthersOnPlay: true*/ /* });*/