import { log } from "./logging.js"; /* A simple function that uses the Crypto.getRandomValues to fill an * array with 32 bit random numbers. */ export const random_numbers = (count) => { let out = new Uint32Array(count); return window.crypto.getRandomValues(out); } /* The stupid web crypto api doesn't have the randomInt function that the * node crypto library has so I have to hack in this complete BS. I really * hate browser manufacturers. */ export const rand_int = (min, max) => { return Math.floor((Math.random() * (max - min)) + min); } /** * Partially taken from https://lea.verou.me/2016/12/resolve-promises-externally-with-this-one-weird-trick/ on * how to externally resolve a promise. You can use this in the very common situation in svelte where you need * to await on some condition, but you can't create the promise until the onMount. Instead start it off with defer() * then resolve it when you're ready. */ export const defer = (debug="") => { let res; let rej; let promise = new Promise((resolve, reject) => { if(debug) { res = (result) => { log.debug("resolved defer", debug); resolve(result); } rej = (error) => { log.debug("REJECT defer", debug); reject(error); } } else { res = resolve; rej = reject; } }); promise.resolve = res; promise.reject = rej; return promise; } export class Mutex { constructor() { this.waiting = []; } get count() { return this.waiting.length; } async attempt() { if(this.waiting.length === 0) { this.waiting.push(defer()); return true; } else { // they don't want to get it so return false return false; } } async hold() { if(this.waiting.length === 0) { this.waiting.push(defer()); return true; } else { const lock = defer(); this.waiting.push(lock); return lock; } } release() { const lock = this.waiting.pop(); lock.resolve(true); } async wait() { await this.hold(); await this.release(); } } /** * Used inside observer actions (see Svelte actions) to create a * cleanup function when the action is destroyed. This by default * calls unobserve on the node, but it will also do a callback if * provided to options. Callbacks are: * * update(node, opts) - Called whenever the values of options is updated, includes the node. * destroy(node) - Called when the action is destroyed, includes the node. * */ const create_observer_cleanup = (observer, node, options) => { const cleaner = { destroy() { if(options.destroy) options.destroy(node); observer.unobserve(node); } } if(options.update) { cleaner.update = (opts) => { options.update(node, opts); } } return cleaner; } export const visibility = (node, opts) => { let options = opts || {}; // NOTE: does JS default values have the ruby problem? const callback = (entries) => { for(let entry of entries) { if(entry.isIntersecting) { node.dispatchEvent(new CustomEvent("visible", { detail: entry })); } else { node.dispatchEvent(new CustomEvent("hidden", { detail: entry })); } } } const vis_observer = new IntersectionObserver(callback, options || {}); vis_observer.observe(node); return create_observer_cleanup(vis_observer, node, options); } export const resize = (node, options) => { const callback = (entries) => { entries.forEach(entry => { node.dispatchEvent(new CustomEvent("resize", { detail: entry })); }); } const resize_observer = new ResizeObserver(callback, options || {}); resize_observer.observe(node); return create_observer_cleanup(resize_observer, node, options); }