import mimicFn from '../data/mimic-fn';
/**
* @module debounce
* @description Returns a debounced function that delays calling the input function until after wait milliseconds
* have elapsed since the last time the debounced function was called.
* It comes with a .cancel() method to cancel any scheduled input function calls.
*
* @since 1.0.0
*
* @param {Function} inputFunction Function to debounce.
* @param {object} options {
* {number} wait Time in milliseconds to wait until the input function is called, default: 0.
* {number} maxWait The maximum time the input function is allowed to be delayed before it's invoked. This can be used to limit the number of calls handled in a constant stream. For example, a media player sending updates every few milliseconds but wants to be handled only once a second, default: infinity.
* {boolean} before Trigger the function on the leading edge of the wait interval. For example, can be useful for preventing accidental double-clicks on a "submit" button from firing a second time, default: false
* {boolean} after Trigger the function on the trailing edge of the wait interval, default: true
* }
*
* @requires mimicFn
*
* @return {Function} The debounced function.
*
* @example
* import { debounce } from "@gravityforms/utils";
*
* function Example() {
* // do something on resize
* }
*
* window.addEventListener( 'resize', debounce( Example, { wait: 400 } ) );
*
*/
export default function debounce( inputFunction, options = {} ) {
if ( typeof inputFunction !== 'function' ) {
throw new TypeError( `Expected the first argument to be a function, got \`${ typeof inputFunction }\`` );
}
const {
wait = 0,
maxWait = Number.Infinity,
before = false,
after = true,
} = options;
if ( ! before && ! after ) {
throw new Error( 'Both `before` and `after` are false, function wouldn\'t be called.' );
}
let timeout;
let maxTimeout;
let result;
const debouncedFunction = function( ...arguments_ ) {
const context = this;
const later = () => {
timeout = undefined;
if ( maxTimeout ) {
clearTimeout( maxTimeout );
maxTimeout = undefined;
}
if ( after ) {
result = inputFunction.apply( context, arguments_ );
}
};
const maxLater = () => {
maxTimeout = undefined;
if ( timeout ) {
clearTimeout( timeout );
timeout = undefined;
}
if ( after ) {
result = inputFunction.apply( context, arguments_ );
}
};
const shouldCallNow = before && ! timeout;
clearTimeout( timeout );
timeout = setTimeout( later, wait );
if ( maxWait > 0 && maxWait !== Number.Infinity && ! maxTimeout ) {
maxTimeout = setTimeout( maxLater, maxWait );
}
if ( shouldCallNow ) {
result = inputFunction.apply( context, arguments_ );
}
return result;
};
mimicFn( debouncedFunction, inputFunction );
debouncedFunction.cancel = () => {
if ( timeout ) {
clearTimeout( timeout );
timeout = undefined;
}
if ( maxTimeout ) {
clearTimeout( maxTimeout );
maxTimeout = undefined;
}
};
return debouncedFunction;
}