Skip to content

Instantly share code, notes, and snippets.

@reynotekoppele
Last active August 2, 2022 13:59
Show Gist options
  • Save reynotekoppele/febdfb22fc180d7025862715c9c2f8fb to your computer and use it in GitHub Desktop.
Save reynotekoppele/febdfb22fc180d7025862715c9c2f8fb to your computer and use it in GitHub Desktop.
Javascript utilities
/* global ajax_object */
/**
* Make AJAX request to WordPress.
*
* @param {RequestInit} [options] Fetch options.
* @param {AbortController} [controller] Controller for current request.
*
* @throws Will throw an error if the status code is not 2xx.
* @throws Will throw data if the WordPress success status is false.
*
* @return {Promise<any>} Resolved data.
*/
export const doRequest = async ( options = {}, controller = null ) => {
const response = await fetch( ajax_object.ajax_url, { // eslint-disable-line camelcase
method: 'POST',
controller,
...options,
} );
// Make sure we have a 2xx status code.
if ( ! response.ok ) {
throw new Error( 'Response is not ok!' );
}
// Get JSON response.
const { success, data } = await response.json();
// Make sure the response is successful.
if ( ! success ) {
throw data;
}
// Return data.
return data;
};
/* ========================= */
/**
* Wait for a given amount of time.
*
* @param {number} ms Number of milliseconds to wait.
*
* @return {Promise<void>}
*/
export const sleep = ( ms ) => {
return new Promise( ( res ) => setTimeout( res, ms ) )
};
/* ========================= */
/**
* Execute function after given delay, defaults to 500ms.
* Reset delay when called within the given timeframe.
*
* @param {Function} cb Callback function to execute.
* @param {number} ms Number of milliseconds to wait.
*/
export const debounce = ( cb, ms = 500 ) => {
let timerId;
return ( ...args ) => {
clearTimeout( timerId );
timerId = setTimeout( () => {
cb( ...args );
}, ms );
};
};
/* ========================= */
/**
* Execute function once every given timeframe, defaults to 500ms.
* Can be used as a rate limiter.
*
* @param {Function} cb Callback function to execute.
* @param {number} ms Number of milliseconds to wait.
*/
export const throttle = ( cb, ms = 500 ) => {
let timerId;
let lastRun;
const runCallback = ( ...args ) => {
cb( ...args );
lastRun = Date.now();
};
return ( ...args ) => {
if ( ! lastRun ) {
runCallback( ...args );
return;
}
clearTimeout( timerId );
timerId = setTimeout( () => {
if ( ( Date.now() - lastRun ) >= ms ) {
runCallback( ...args );
}
}, ms - ( Date.now() - lastRun ) );
};
};
/* ========================= */
/**
* Observe property changes on external properties.
*
* WARNING: Only use this when you can't wrap the object in a Proxy (see {@link observeInternalProperty}).
*
* @param {Object} obj Object to observe.
* @param {string} property Key of property to observe.
* @param {Function} onChanged Callback to fire when the property changes.
*/
export const observeExternalProperty = ( obj, property, onChanged ) => {
// Get descriptor settings.
const oldDescriptor = Object.getOwnPropertyDescriptor( obj, property );
// Make a copy of the value.
let val = obj[ property ];
Object.defineProperty( obj, property, {
get() {
return val;
},
set( value ) {
val = value;
onChanged( value );
},
enumerable: oldDescriptor?.enumerable,
configurable: oldDescriptor?.configurable,
} );
};
/* ========================= */
/**
* Observe property changes on internal object.
*
* @param {Object} obj Object to observe.
* @param {string} property Key of property to observe.
* @param {Function} onChanged Callback to fire when the property changes.
*
* @return {Proxy} Proxied object.
*/
export const observeInternalProperty = ( obj, property, onChanged ) => {
const handler = {
get() {
return Reflect.get( ...arguments );
},
set( target, prop, value ) {
Reflect.set( ...arguments );
if ( prop === property ) {
return onChanged( value );
}
},
};
return new Proxy( obj, handler );
};
/* ========================= */
/**
* Track multiple instances of a class.
*/
export class InstanceTracker {
/** @type {WeakMap<HTMLElement, InstanceTracker>} */
static #instances = new WeakMap();
/**
* Initialize and store class instance.
*
* @param {HTMLElement} element - Element associated with instance.
*/
constructor( element ) {
InstanceTracker.#instances.set( element, this );
}
/**
* Get class instance of given element
*
* @param {HTMLElement} element - Element associated with instance.
*
* @return {InstanceTracker} instance of our class.
*/
static getInstance( element ) {
return InstanceTracker.#instances.get( element );
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment