export function mapFromObject(obj: {[key: string]: T}): Map { const ret = new Map(); for (const k of Object.keys(obj)) ret.set(k, obj[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 function get(url: string): Promise { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); const handleError = () => { if (xhr.readyState !== xhr.DONE) { reject(new Error('HTTP request ended in state ' + xhr.readyState)); return; } reject(new Error('HTTP request returned status ' + xhr.status)); }; xhr.addEventListener('error', handleError); xhr.addEventListener('load', () => { if (xhr.readyState !== xhr.DONE || xhr.status !== 200) { handleError(); return; } resolve(xhr); }); xhr.open('GET', url, true); xhr.send(); }); } export async function getJSON(url: string): Promise { return JSON.parse((await get(url)).responseText); }