export function recordToMap(r: Record): Map { const ret = new Map(); for (const k of Object.keys(r)) ret.set(k, r[k]); return ret; } export function mapValues(f: (v: V1) => V2, map: Map): Map { const ret: Map = new Map(); for (const [k, v] of map) ret.set(k, f(v)); return ret; } export async function mapValuesAsync(f: (v: V1) => Promise, map: Map): Promise> { const ret: Map = new Map(); for (const [k, v] of mapValues(f, map)) ret.set(k, await v); return ret; } export function nextPowerOf2(n: number): number { let i = 1; while (i < n) i *= 2; return i; } export class Listenable { private readonly listeners: Array<(...args: T) => void> = []; public addListener(listener: (...args: T) => void): void { this.listeners.push(listener); } protected runListeners(...args: T): void { this.listeners.forEach((l) => l(...args)); } } export async function getJSON(url: string): Promise { const res = await window.fetch(url); if (res.status < 200 || res.status >= 300) throw new Error(res.statusText); return await res.json(); } export function nextAnimationFrame(): Promise { return new Promise((resolve) => window.requestAnimationFrame(resolve)); }