function validPath(path, candidates) { if (typeof path !== "string" || !/^\/[^,\n]+$/.test(path)) return false; return (candidates || []).includes(path); } function validCollection(paths, candidates) { const result = []; const seen = {}; for (const path of paths || []) { if (!validPath(path, candidates) || seen[path]) continue; seen[path] = true; result.push(path); } return result; } function validAssignments(assignments, candidates) { const result = {}; if (!assignments || typeof assignments !== "object" || Array.isArray(assignments)) return result; for (const output of Object.keys(assignments)) { if (!/^[A-Za-z0-9_.-]+$/.test(output)) continue; const path = assignments[output]; if (validPath(path, candidates)) result[output] = path; } return result; } function effectiveMap(mode, globalPath, slideshowPath, assignments, outputs, candidates) { const result = {}; const fallback = validPath(globalPath, candidates) ? globalPath : ((candidates || [])[0] || ""); const selectedAssignments = validAssignments(assignments, candidates); const slideshow = validPath(slideshowPath, candidates) ? slideshowPath : fallback; for (const output of outputs || []) { if (typeof output !== "string" || !/^[A-Za-z0-9_.-]+$/.test(output)) continue; if (mode === "per-monitor") result[output] = selectedAssignments[output] || fallback; else if (mode === "slideshow") result[output] = slideshow; else result[output] = fallback; } return result; } function orderedNext(collection, current) { const paths = collection || []; if (paths.length === 0) return ""; const index = paths.indexOf(current); return paths[(index + 1 + paths.length) % paths.length]; } function shuffledBag(collection, random) { const result = Array.from(collection || []); const nextRandom = typeof random === "function" ? random : Math.random; for (let index = result.length - 1; index > 0; index--) { const swap = Math.floor(Math.max(0, Math.min(0.999999999, nextRandom())) * (index + 1)); const value = result[index]; result[index] = result[swap]; result[swap] = value; } return result; } function shuffledNext(collection, bag, current, random) { const paths = Array.from(collection || []); if (paths.length === 0) return { path: "", bag: [] }; const remaining = []; const seen = {}; for (const path of bag || []) { if (!paths.includes(path) || seen[path]) continue; seen[path] = true; remaining.push(path); } if (remaining.length === 0) remaining.push(...shuffledBag(paths, random)); if (remaining.length > 1 && remaining[0] === current) { const replacement = remaining.findIndex(path => path !== current); const value = remaining[0]; remaining[0] = remaining[replacement]; remaining[replacement] = value; } return { path: remaining[0], bag: remaining.slice(1) }; }