This is the template project that's checked out and configured when you run the bando-up command from ljsthw-bandolier. This is where the code really lives.
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.
 
 
 
 
bandolier-template/client/helpers.js

152 lines
3.7 KiB

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);
}