Source: Function.js

'use strict';
if ( ( 0, eval )( 'this' ).XtraUtils && ( 0, eval )( 'this' ).XtraUtils.Utility ) {
	/**
	 * @type {Utility}
	 * @property {function} aidsIn The Functionw class
	 * @property {Function} aidsIn.prototype the Function prototype
	 * @namespace
	 * @memberof XtraUtils
	 */
	XtraUtils.Function = new XtraUtils.Utility( Function );

	const { keys } = Object,
		{ isArray } = Array,

		rename = ( a,b )=>Function( 'fn',`return (function ${b}(){\n	return fn.apply(this, arguments)\n});` )( a );

	XtraUtils.Function.addMethod( 'flow', ( function(){
		/**
		 * Applies functions in order of the arguments passed to `Function.flow`.
		 * @param {...Function} functs The functions to flow. Each function should return an array of arguments to pass to the next function, or a single value.
		 * @returns {Function} The resulting built function.
		 * @memberof XtraUtils.Function.
		 * @example
		 * function add(a,b) { return a + b };
		 * function square(a) { return a ** 2};
		 * addSquare = Function.flow(add, sqaure);
		 * addSquare(1,2);		// 9
		 */
		function flow( ...functs ) {
			return rename( function(){
				let args = arguments, value = undefined;
				functs.forEach( ( funct, _ ) => {
					value = funct( ...args );
					args = [isArray( value ) ? [...value] : value];
				} );
				return value;
			}, 'flowed' );
		}
		return flow;
	} )() );

	XtraUtils.Function.addMethod( 'reArg', ( function(){
		const _toString = Object.prototype.toString,
			_isNumber = val => typeof val === 'number',
			_reOrg = ( a,c )=>{let b = Array( a.length ).fill();a.forEach( ( a,d )=>{b[c[d]] = a;} );return b;},
			_regularize = ( a )=>{let b = [];if( '[object Array]' === _toString.call( a ) ){return a;}keys( a ).filter( _isNumber ).forEach( ( c )=>{b[c] = a[c];} );return b;};
		/**
		 * Re-arranges the arguments to a function.
		 * @param {Function} fn The function to remap the arguments.
		 * @param {Array.<number>|Object<number,number>} indexes The way to map the arguments.
		 * @param {Object} [context=null] The context to set the `this` value to.
		 * @returns {Function} The resulting re-mapped function!
		 * @memberof XtraUtils.Function.
		 * @example
		 * function j(a,b,c) { return `${a}+${b}+${c}` };
		 * j = Function.reArg(j, [2, 1, 0])
		 * j(1,2,3);						 // '3+2+1'
		 */
		function reArg( fn, indexes, context = null ) {
			let func = fn.bind( context );
			indexes = _regularize( indexes );
			return rename( function( ...args ) {
				return func( ..._reOrg( args,indexes ) );
			}, fn.name );
		}
		return reArg;
	} )() );

	XtraUtils.Function.addMethod( 'template', ( function(){
		const _toString = Object.prototype.toString,
			_isNumber = val => typeof val === 'number',
			_reOrg = ( c,a,d )=>{let b = [...c];a = [...a];d.forEach( ( c,d )=>{b.splice( c,1,a[d] );a.shift();} );b.splice( b.length,0,...a );return b;},
			_regularize = ( a )=>{let b = [];if( '[object Array]' === _toString.call( a ) ){return a;}keys( a ).filter( _isNumber ).forEach( ( c )=>{b[c] = a[c];} );return b;};
		/**
		 * Templates a function, enabling you to set static arguments and placeholders.
		 * @param {Function} fn The function to template.
		 * @param {Object} [context=null] The context to operate the function under.
		 * @param {...*} BoundArgs The arguments to revert to static, use '_PH_' for placeholders.
		 * @returns {Function} The resulting function.
		 * @memberof XtraUtils.Function.
		 * @example
		 * function j(a,b,c) { return a + '+' + b + '+' + c };
		 * j = Function.template(j, null, 3, '_PH_', 2);
		 * j(4);		// '3+4+2'
		 * j(8);		// '3+8+2'
		 */
		function template( fn, context = null, ...BoundArgs ) {
			const func = fn.bind( context ),
				placeholders = [],
				indexes = [];
			BoundArgs.forEach( ( item, index ) => {
				if( item === '_PH_' ) {
					placeholders.push( index );
				}
			} );
			return rename( function( ...args ) {
				return func( ..._reOrg( BoundArgs, args, placeholders ) );
			}, fn.name );
		}
		return template;
	} )() );

	XtraUtils.Function.addMethod( 'capArgs', ( function(){
		/**
		 * Limits the number of arguments that can be used by a function.
		 * @param {Function} fn The function to cap the number of arguments on.
		 * @param {number} [cap=fn.length] The cap of the number of args.
		 * @param {Object} [context=null] The context to run the function on.
		 * @returns {Function} The function with arguments capped.
		 * @memberof XtraUtils.Function.
		 * @example
		 * function j(a,b,c) { return a + '+' + b + '+' + 'c' };
		 * j = Function.capArgs(j, 2);
		 * j(1,2,3);		// '1+2+undefined'
		 */
		function capArgs( fn, cap = fn.length, context = null ) {
			const func = fn.bind( context );
			return rename( function( ...args ) {
				args.length = cap;
				return func( ...args );
			}, fn.name );
		}
		return capArgs;
	} )() );

	XtraUtils.Function.addMethod( 'memoize', ( function(){
		/**
		 * Caches the result. Should only be used on pure functions, which return the same result for the same input.
		 * @param {Function} fn The function to cache.
		 * @param {Object} [context=null] The context to operate in.
		 * @returns {Function} The resulting function.
		 * @memberof XtraUtils.Function.
		 */
		function memoize( fn, context = null ){
			//======================================================
			// Many thanks to @ConorO'Brien for allowing me to use.
			// The file this is used from is located here:
			// https://github.com/ConorOBrien-Foxx/ffuncs/blob/master/backend/memoize.js
			// github profile located here:
			// https://github.com/ConorOBrien-Foxx
			//======================================================
			const func = fn.bind( context );
			let mem = new Map();
			return rename( function( ...args ) {
				return (
					!mem.has( args.join( ',' ) ) && mem.set( args.join( ',' ), func( ...args ) ),
					mem.get( args.join( ',' ) )
				);
			}, fn.name );
		}
		return memoize;
	} )() );

	XtraUtils.Function.addMethod( 'throttle', ( function(){
		/**
		 * Delayes the execution of a function. You call it with the normal arguments you would. If you don't wait until the timout expires, this also works as a debounce method.
		 * @param {Function} fn The function to throttle.
		 * @param {number} time The delay to execute the function after.
		 * @param {Object} [context=null] What context to execute under.
		 * @returns {Function} The throttled function.
		 * @memberof XtraUtils.Function.
		 */
		function throttle( fn, time, context = null ){
			const func = fn.bind( context );
			let timeout = -1;
			return rename( function( ...args ) {
				clearTimeout( timeout );
				timeout = setTimeout( () => {
					func( ...args );
				}, time );
			}, fn.name );
		}
		return throttle;
	} )() );

	XtraUtils.Function.addMethod( 'curry', ( function(){
		//======================================================
		// Many thanks to @ConorO'Brien for allowing me to use.
		// The file this is used from is located here:
		// https://github.com/ConorOBrien-Foxx/ffuncs/blob/master/backend/curry.js
		//======================================================
		const currier = ( fn, arity = fn.length, context = null ) => {
			const func = fn.bind( context );
			args = [];
			let _rec = ( ...a ) => {
				args.push( ...a );
				if ( args.length === arity ) {
					return func( ...args );
				} else {
					( ...n ) => _rec( ...n );
				}
			};
			return ( ...n ) => _rec( ...n );
		};
		/**
		 * Allows the function to be called many times, before executing.
		 * @param {Function} fn The function to curry.
		 * @param {number} [arity=fn.length] The number of arguments to execute it at.
		 * @param {Object} [context=null] The context to execute in.
		 * @returns {Function} The curried function.
		 * @memberof XtraUtils.Function.
		 * @example
		 * function j(a,b,c) { return [a,b,c] };
		 * j = Function.curry(j);
		 * j(1,2)(3);		// [1,2,3]
		 * j(1)(2,3);		// [1,2,3]
		 * j(1)(2)(3);	 // [1,2,3]
		 */
		function curry( fn, arity = fn.arity ){
			return rename( currier( fn, arity ), fn.name );
		}
		return curry;
	} )() );

	XtraUtils.Function.addUtil( 'once', ( function(){
		/**
		 * Restricts the execution of a function to one call. Note this does not change the original function.
		 * @param {Function} fn The function to limit.
		 * @param {Object} [context=null] The context to use.
		 * @returns {Function} The restricted function.
		 * @memberof XtraUtils.Function.
		 * @example
		 * function k(a) { return a };
		 * k = Function.once(k);
		 * k(3);		// 3
		 * k(4);		// 4
		 */
		function once( fn, context = null ) {
			const func = fn.bind( context );
			called = 0, result = null;
			return rename( function( ...args ) {
				result = called === 1 ? result : func( ...args );
				called |= 1;
				return result;
			}, fn.name );
		}
	} )() );

	XtraUtils.Function.addMethod( 'afterNCalls', ( function(){
		/**
		 * Returns a function that doesn't execute unless called `n` or more times.
		 * @param {Function} callback The function to restrict.
		 * @param {number} n The number that determines how many times to wait.
		 * @param {Object} [context=null] The context to operate thome.
		 * @returns {Function} The restricted function.
		 * @memberof Xtrautils.Function.
		 */
		function afterNCalls( callback, n, context = null ) {
			let func = callback.bind( context ),
				calls = 0;
			return ( ...args ) => {
				if( calls++ >= times ) {
					return func( ...args );
				}
			};
		}
		return afterNCalls;
	} )() );

} else {
	console.warn( 'XtraUtils is not defined. For more details, please visit ' +
						   'https://github.com/FreezePhoenix/XtraUtils/. If your issue still occurs, submit an issue here:' +
						   'https://github.com/FreezePhoenix/XtraUtils/issues/new' );
}
/**
 * Used to denote that the function has been modified, and may not be a pure function.
 * @alias ModifiedFunction
 * @typedef {Function} ModifiedFunction
 * @memberof XtraUtils.Function.
 */