UNPKG

funcs-js

Version:

Function wrappers for enhanced behavior.

353 lines (316 loc) 10.3 kB
/*global module: false, define: false */ /** * Function wrappers and utilities for enhanced behavior. * * @name funcs * @namespace funcs * @author Sagie Gur-Ari */ /** * Initializes the funcs API. * * @function * @memberof! funcs * @alias funcs.initFuncs * @private * @param {Object} global - The root context (window/global/...) * @param {function} factory - Returns a new instance of the API * @returns {Object} New instance of the API */ (function initFuncs(global, factory) { 'use strict'; const funcs = factory(); /*istanbul ignore next*/ /** * Initializes the funcs API (only used for testing). * * @function * @memberof! funcs * @alias funcs.initFuncsFromContext * @private * @param {Object} context - The root context (window/global/...) * @returns {Object} New instance of the API */ funcs.initFuncsFromContext = function (context) { return initFuncs(context, factory); }; /*istanbul ignore next*/ if ((typeof define === 'function') && define.amd) { define(function defineLib() { return funcs; }); } else if ((typeof module === 'object') && module.exports) { module.exports = funcs; } else { global.funcs = funcs; } return funcs; }(this, function initFuncs() { 'use strict'; const funcs = {}; /** * Adds chaining support for the provided function. * * @function * @memberof! funcs * @alias funcs.addChaining * @private * @param {function} fn - Adds the funcs APIs to the provided function with this function as a context */ const addChaining = function (fn) { /** * Chain function. * * @function * @memberof! funcs * @alias funcs.maxTimesChain * @private * @param {Number} times - The max times the provided function will be invoked * @returns {function} The new wrapper function */ fn.maxTimes = function (times) { return funcs.maxTimes(fn, times); }; /** * Chain function. * * @function * @memberof! funcs * @alias funcs.onceChain * @private * @returns {function} The new wrapper function */ fn.once = function () { return funcs.once(fn); }; /** * Chain function. * * @function * @memberof! funcs * @alias funcs.delayChain * @private * @param {Number} delay - The invocation delay in millies * @returns {function} The new wrapper function */ fn.delay = function (delay) { return funcs.delay(fn, delay); }; /** * Chain function. * * @function * @memberof! funcs * @alias funcs.asyncChain * @private * @returns {function} The new wrapper function */ fn.async = function () { return funcs.async(fn); }; }; /** * Empty function. * * @function * @memberof! funcs * @alias funcs.noop * @public * @returns {undefined} Undefined */ funcs.noop = function () { return undefined; }; /** * Returns true if the provided argument is a function. * * @function * @memberof! funcs * @alias funcs.isFunction * @public * @param {function} [fn] - The function to check * @returns {Boolean} True if the provided argument is a function * @example * ````js * const isFn = funcs.isFunction(myFunction); * * funcs.isFunction(function () {}); //true * funcs.isFunction(); //false * funcs.isFunction(5); //false * funcs.isFunction(true); //false * ```` */ funcs.isFunction = function (fn) { return (fn && (typeof fn === 'function')) || false; }; /** * Ensures a return function.<br> * If a function is provided, it will be returned, otherwise a noop function will be returned. * * @function * @memberof! funcs * @alias funcs.ensure * @public * @param {function} [fn] - The function to check * @returns {function} The original function if provided, or a noop * @example * ````js * const handler = funcs.ensure(maybeHandler); * ```` */ funcs.ensure = function (fn) { if (!this.isFunction(fn)) { return funcs.noop; } return fn; }; /** * Wraps the provided function and ensures it is invoked no more than the provided amount.<br> * This function output can be chained with other funcs apis. * * @function * @memberof! funcs * @alias funcs.maxTimes * @public * @param {function} fn - The function to wrap * @param {Number} times - The max times the provided function will be invoked * @param {Object} [options] - see details * @param {Boolean} [options.callbackStyle=false] - If true, the provided function will only get the first 2 arguments (will improve runtime performance) * @returns {function} The new wrapper function * @example * ````js * const onlyOnceCallback = funcs.maxTimes(callback, 1); * * //can also chain multiple modifications (chained functions do not require original function as argument) * const delayedMaxTimesCallback = funcs.maxTimes(callback, 5).delay(500); * ```` */ funcs.maxTimes = function (fn, times, options) { if ((!this.isFunction(fn)) || (!times) || (typeof times !== 'number') || (times < 0)) { return this.noop; } let callbackStyle = false; if (options && options.callbackStyle) { callbackStyle = true; } let counter = 0; const fnMaxTimesWrapper = function (arg1, arg2) { if (counter < times) { counter++; if (callbackStyle) { return fn(arg1, arg2); } if (!arguments.length) { return fn(); } return fn.apply(null, arguments); } }; //add chaining support addChaining(fnMaxTimesWrapper); return fnMaxTimesWrapper; }; /** * Ensures the provided function is invoked only once.<br> * This is the same as calling funcs.maxTimes(fn, 1)<br> * This function output can be chained with other funcs apis. * * @function * @memberof! funcs * @alias funcs.once * @public * @param {function} fn - The function to wrap * @param {Object} [options] - see details * @param {Boolean} [options.callbackStyle=false] - If true, the provided function will only get the first 2 arguments (will improve runtime performance) * @returns {function} The new wrapper function * @example * ````js * const onlyOnceCallback = funcs.once(callback); * * //can also chain multiple modifications (chained functions do not require original function as argument) * const asyncOnceCallback = funcs.once(callback).async(); * ```` */ funcs.once = function (fn, options) { return this.maxTimes(fn, 1, options); }; /** * Trigger the actual function only after the provided delay.<br> * This function output can be chained with other funcs apis. * * @function * @memberof! funcs * @alias funcs.delay * @public * @param {function} fn - The function to wrap * @param {Number} [delay=0] - The invocation delay in millies * @param {Object} [options] - see details * @param {Boolean} [options.callbackStyle=false] - If true, the provided function will only get the first 2 arguments (will improve runtime performance) * @returns {function} The new wrapper function * @example * ````js * const delayedCallback = funcs.delay(callback, 500); * * //can also chain multiple modifications (chained functions do not require original function as argument) * const delayedMaxTimesCallback = funcs.delay(callback, 500).maxTimes(5); * ```` */ funcs.delay = function (fn, delay, options) { if (!this.isFunction(fn)) { return this.noop; } //if delay not provided, but options were provided if (delay && (typeof delay === 'object')) { options = delay; delay = 0; } delay = delay || 0; if (delay < 0) { return fn; } let callbackStyle = false; if (options && options.callbackStyle) { callbackStyle = true; } const fnDelayWrapper = function (arg1, arg2) { const fnArguments = arguments; setTimeout(function postDelay() { if (callbackStyle) { fn(arg1, arg2); } else if (!fnArguments.length) { fn(); } else { fn.apply(null, fnArguments); } }, delay); }; //add chaining support addChaining(fnDelayWrapper); return fnDelayWrapper; }; /** * Ensures the function is invoked only in the next cycle.<br> * This is the same as calling funcs.delay(fn, 0)<br> * This function output can be chained with other funcs apis. * * @function * @memberof! funcs * @alias funcs.async * @public * @param {function} fn - The function to wrap * @param {Object} [options] - see details * @param {Boolean} [options.callbackStyle=false] - If true, the provided function will only get the first 2 arguments (will improve runtime performance) * @returns {function} The new wrapper function * @example * ````js * const asyncCallback = funcs.async(callback); * * //can also chain multiple modifications (chained functions do not require original function as argument) * const asyncOnceCallback = funcs.async(callback).once(); * ```` */ funcs.async = function (fn, options) { return this.delay(fn, 0, options); }; return funcs; }));