/** section: Language * class Function * * Extensions to the built-in `Function` object. **/ Object.extend(Function.prototype, (function() { var slice = Array.prototype.slice; function update(array, args) { var arrayLength = array.length, length = args.length; while (length--) array[arrayLength + length] = args[length]; return array; } function merge(array, args) { array = slice.call(array, 0); return update(array, args); } /** * Function#argumentNames() -> Array * * Reads the argument names as stated in the function definition and returns * the values as an array of strings (or an empty array if the function is * defined without parameters). * * ### Examples * * function fn(foo, bar) { * return foo + bar; * } * fn.argumentNames(); * //-> ['foo', 'bar'] * * Prototype.emptyFunction.argumentNames(); * //-> [] **/ function argumentNames() { var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1] .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '') .replace(/\s+/g, '').split(','); return names.length == 1 && !names[0] ? [] : names; } /** * Function#bind(object[, args...]) -> Function * - object (Object): The object to bind to. * * Wraps the function in another, locking its execution scope to an object * specified by `object`. **/ function bind(context) { if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this; var __method = this, args = slice.call(arguments, 1); return function() { var a = merge(args, arguments); return __method.apply(context, a); } } /** related to: Function#bind * Function#bindAsEventListener(object[, args...]) -> Function * - object (Object): The object to bind to. * * An event-specific variant of [[Function#bind]] which ensures the function * will recieve the current event object as the first argument when * executing. **/ function bindAsEventListener(context) { var __method = this, args = slice.call(arguments, 1); return function(event) { var a = update([event || window.event], args); return __method.apply(context, a); } } /** * Function#curry(args...) -> Function * Partially applies the function, returning a function with one or more * arguments already "filled in." * * Function#curry works just like [[Function#bind]] without the initial * scope argument. Use the latter if you need to partially apply a function * _and_ modify its execution scope at the same time. **/ function curry() { if (!arguments.length) return this; var __method = this, args = slice.call(arguments, 0); return function() { var a = merge(args, arguments); return __method.apply(this, a); } } /** * Function#delay(seconds[, args...]) -> Number * - seconds (Number): How long to wait before calling the function. * * Schedules the function to run after the specified amount of time, passing * any arguments given. * * Behaves much like `window.setTimeout`. Returns an integer ID that can be * used to clear the timeout with `window.clearTimeout` before it runs. * * To schedule a function to run as soon as the interpreter is idle, use * [[Function#defer]]. **/ function delay(timeout) { var __method = this, args = slice.call(arguments, 1); timeout = timeout * 1000 return window.setTimeout(function() { return __method.apply(__method, args); }, timeout); } /** * Function#defer(args...) -> Number * Schedules the function to run as soon as the interpreter is idle. * * A "deferred" function will not run immediately; rather, it will run as soon * as the interpreter's call stack is empty. * * Behaves much like `window.setTimeout` with a delay set to `0`. Returns an * ID that can be used to clear the timeout with `window.clearTimeout` before * it runs. **/ function defer() { var args = update([0.01], arguments); return this.delay.apply(this, args); } /** * Function#wrap(wrapperFunction) -> Function * - wrapperFunction (Function): The function to act as a wrapper. * * Returns a function "wrapped" around the original function. * * `Function#wrap` distills the essence of aspect-oriented programming into * a single method, letting you easily build on existing functions by * specifying before and after behavior, transforming the return value, or * even preventing the original function from being called. **/ function wrap(wrapper) { var __method = this; return function() { var a = update([__method.bind(this)], arguments); return wrapper.apply(this, a); } } /** * Function#methodize() -> Function * Wraps the function inside another function that, at call time, pushes * `this` to the original function as the first argument. * * Used to define both a generic method and an instance method. **/ function methodize() { if (this._methodized) return this._methodized; var __method = this; return this._methodized = function() { var a = update([this], arguments); return __method.apply(null, a); }; } return { argumentNames: argumentNames, bind: bind, bindAsEventListener: bindAsEventListener, curry: curry, delay: delay, defer: defer, wrap: wrap, methodize: methodize } })());