You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
153 lines
3.7 KiB
153 lines
3.7 KiB
2 years ago
|
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);
|
||
|
}
|