// MusicRoom - Utilities module // DOM helpers, formatting, toast notifications (function() { const M = window.MusicRoom; // DOM selector helper M.$ = (s) => document.querySelector(s); // Format seconds as m:ss M.fmt = function(sec) { if (!sec || !isFinite(sec)) return "0:00"; const m = Math.floor(sec / 60); const s = Math.floor(sec % 60); return m + ":" + String(s).padStart(2, "0"); }; // Toast notifications M.showToast = function(message, duration = 4000) { const container = M.$("#toast-container"); const toast = document.createElement("div"); toast.className = "toast"; toast.textContent = message; container.appendChild(toast); setTimeout(() => { toast.classList.add("fade-out"); setTimeout(() => toast.remove(), 300); }, duration); }; // Flash permission denied animation M.flashPermissionDenied = function() { const row = M.$("#progress-row"); row.classList.remove("denied"); void row.offsetWidth; // Trigger reflow to restart animation row.classList.add("denied"); setTimeout(() => row.classList.remove("denied"), 500); }; // Get current server time (extrapolated) M.getServerTime = function() { if (M.serverPaused) return M.serverTimestamp; return M.serverTimestamp + (Date.now() - M.lastServerUpdate) / 1000; }; // Check if current user can control playback M.canControl = function() { if (!M.currentUser) return false; if (M.currentUser.isAdmin) return true; return M.currentUser.permissions?.some(p => p.resource_type === "stream" && (p.resource_id === M.currentStreamId || p.resource_id === null) && p.permission === "control" ); }; })();