function initCustomAudioPlayers() {
// =================== Config & Helpers ===================
const CONFIG = {
LEFT_MARGIN_PERCENT: 6,
RIGHT_MARGIN_PERCENT: 9,
PROGRESS_BAR_UPDATE_INTERVAL: 20,
BUFFER_TIME: 0.1,
};
const debounce = (func, wait) => {
let timeout;
return (...args) => {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
};
const icons = {
play: ``,
pause: ``,
dots: ``,
spinner: `
`,
error: ``,
};
// For user preferences (gain, filters, etc.)
const safeGet = (k, fb) => {
try {
return localStorage.getItem(k) || fb;
} catch {
return fb;
}
};
const safeSet = (k, v) => {
try {
localStorage.setItem(k, v);
} catch {}
};
// Helper for readable/friendly display of numbers
const compactFormatter = new Intl.NumberFormat(undefined, { notation: 'compact' });
const formatCompactOrEcho = (value) => {
const n = (typeof value === 'number') ? value : Number(value);
if (!Number.isFinite(n)) {
return String(value);
}
const out = compactFormatter.format(n);
return out;
};
// Retrieve saved user preferences
const savedGain = safeGet("customAudioPlayerGain", "Off");
const savedHighpass = safeGet("customAudioPlayerFilterHigh", "Off");
const savedLowpass = safeGet("customAudioPlayerFilterLow", "Off");
// Helper to apply multiple style properties
const applyStyles = (elem, styles) => Object.assign(elem.style, styles);
// Basic styling for small control buttons
const styleButton = (btn, styles = {}) => {
applyStyles(btn, styles);
// Subtle hover highlight
btn.addEventListener("mouseover", () => (btn.style.background = "rgba(255,255,255,0.1)"));
btn.addEventListener("mouseout", () => (btn.style.background = "transparent"));
};
// Helper to create a button
const createButton = (
parent,
{ text = "", html = "", styles = {}, data = {}, onClick = null } = {}
) => {
const btn = document.createElement("button");
btn.type = "button";
if (text) btn.textContent = text;
if (html) btn.innerHTML = html;
Object.entries(data).forEach(([k, v]) => (btn.dataset[k] = v));
styleButton(btn, styles);
if (onClick) btn.addEventListener("click", onClick);
parent.appendChild(btn);
return btn;
};
// Common small‐button styling
const iconBtnStyle = {
background: "transparent",
border: "none",
cursor: "pointer",
width: "36px",
height: "36px",
display: "flex",
alignItems: "center",
justifyContent: "center",
marginRight: "0.6rem",
padding: "0",
borderRadius: "50%",
};
const textBtnStyle = {
background: "none",
border: "none",
cursor: "pointer",
color: "white",
fontSize: "14px",
textAlign: "right",
width: "100%",
padding: "6px 12px",
margin: "2px 0",
borderRadius: "4px",
};
const optionBtnStyle = {
background: "none",
border: "none",
cursor: "pointer",
color: "white",
fontSize: "14px",
textAlign: "center",
width: "auto",
padding: "6px 8px",
margin: "2px 4px",
borderRadius: "4px",
};
// =================== Main Loop over all .custom-audio-player ===================
document.querySelectorAll(".custom-audio-player").forEach((player) => {
let hasLoaded = false; // set to true once metadata is available
// Audio/player data
const audioSrc = player.dataset.audioSrc;
const imageSrc = player.dataset.imageSrc;
//