81 lines
2.7 KiB
JavaScript
81 lines
2.7 KiB
JavaScript
// 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);
|
|
};
|
|
|
|
// Set track title (UI and document title)
|
|
M.setTrackTitle = function(title) {
|
|
M.currentTitle = title;
|
|
const titleEl = M.$("#track-title");
|
|
const containerEl = M.$("#track-name");
|
|
const marqueeEl = containerEl.querySelector(".marquee-inner");
|
|
|
|
titleEl.textContent = title;
|
|
document.title = title ? `${title} - MusicRoom` : "MusicRoom";
|
|
|
|
// Check if title overflows and needs scrolling
|
|
requestAnimationFrame(() => {
|
|
const needsScroll = titleEl.scrollWidth > containerEl.clientWidth;
|
|
containerEl.classList.toggle("scrolling", needsScroll);
|
|
|
|
// Duplicate text for seamless wrap-around scrolling
|
|
if (needsScroll) {
|
|
marqueeEl.innerHTML = `<span id="track-title">${title}</span><span class="marquee-spacer"> • </span><span>${title}</span><span class="marquee-spacer"> • </span>`;
|
|
} else {
|
|
marqueeEl.innerHTML = `<span id="track-title">${title}</span>`;
|
|
}
|
|
});
|
|
};
|
|
|
|
// 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 === "channel" &&
|
|
(p.resource_id === M.currentChannelId || p.resource_id === null) &&
|
|
p.permission === "control"
|
|
);
|
|
};
|
|
})();
|