Skip to main content

max / makenotwork

1.8 KB · 37 lines History Blame Raw
1 const audio = new Audio();
2 let loaded = false;
3 // Read the preview URL from the data- attribute rather than interpolating it
4 // into this JS string. Askama autoescapes for HTML, which is the correct
5 // escaper for an attribute value but NOT for a JS string literal — keeping the
6 // URL in the attribute keeps escaping correct even once preview URLs derive
7 // from user-influenced filenames.
8 const previewUrl = document.querySelector('.player').dataset.previewUrl;
9 function togglePlay() {
10 if (!loaded) { audio.src = previewUrl; loaded = true; }
11 if (audio.paused) { audio.play(); document.getElementById('play').innerHTML = '▮▮'; }
12 else { audio.pause(); document.getElementById('play').innerHTML = '▶'; }
13 }
14 audio.ontimeupdate = () => {
15 const pct = (audio.currentTime / audio.duration) * 100;
16 document.getElementById('progress').style.width = pct + '%';
17 const m = Math.floor(audio.currentTime / 60);
18 const s = Math.floor(audio.currentTime % 60);
19 document.getElementById('time').textContent = m + ':' + (s < 10 ? '0' : '') + s;
20 };
21 audio.onended = () => { document.getElementById('play').innerHTML = '&#9654;'; };
22 function seek(e) {
23 if (!audio.duration) return;
24 const rect = e.currentTarget.getBoundingClientRect();
25 audio.currentTime = ((e.clientX - rect.left) / rect.width) * audio.duration;
26 }
27
28 // Wire the play/seek controls without inline on* handlers (the embed page runs
29 // under the same CSP that forbids script-src 'unsafe-inline'). This page loads
30 // only this file, not the core module's data-action dispatcher, so bind directly.
31 document.addEventListener('DOMContentLoaded', function () {
32 var play = document.getElementById('play');
33 if (play) play.addEventListener('click', togglePlay);
34 var bar = document.getElementById('progress-bar');
35 if (bar) bar.addEventListener('click', seek);
36 });
37