@qooxdoo/framework
Version:
The JS Framework for Coders
465 lines (414 loc) • 14.5 kB
JavaScript
/* ************************************************************************
qooxdoo - the new era of web development
http://qooxdoo.org
Copyright:
2004-2008 1&1 Internet AG, Germany, http://www.1und1.de
License:
MIT: https://opensource.org/licenses/MIT
See the LICENSE file in the project's top-level directory for details.
Authors:
* Sebastian Werner (wpbasti)
* Andreas Ecker (ecker)
======================================================================
This class contains code based on the following work:
* Mootools
http://mootools.net
Version 1.1.1
Copyright:
2007 Valerio Proietti
License:
MIT: http://www.opensource.org/licenses/mit-license.php
************************************************************************ */
/**
* Collection of helper methods operating on functions.
*
* @ignore(qx.core.Object)
* @require(qx.lang.Array)
*/
qx.Bootstrap.define("qx.lang.Function",
{
statics :
{
/**
* Extract the caller of a function from the arguments variable.
* This will not work in Opera < 9.6.
*
* @param args {arguments} The local arguments variable
* @return {Function} A reference to the calling function or "undefined" if caller is not supported.
*/
getCaller : function(args) {
return args.caller ? args.caller.callee : args.callee.caller;
},
/**
* Try to get a sensible textual description of a function object.
* This may be the class/mixin and method name of a function
* or at least the signature of the function.
*
* @param fcn {Function} function the get the name for.
* @return {String} Name of the function.
*/
getName : function(fcn)
{
if (fcn.displayName) {
return fcn.displayName;
}
if (fcn.$$original || fcn.wrapper || fcn.classname) {
return fcn.classname + ".constructor()";
}
if (fcn.$$mixin)
{
//members
for(var key in fcn.$$mixin.$$members)
{
if (fcn.$$mixin.$$members[key] == fcn) {
return fcn.$$mixin.name + ".prototype." + key + "()";
}
}
// statics
for(var key in fcn.$$mixin)
{
if (fcn.$$mixin[key] == fcn) {
return fcn.$$mixin.name + "." + key + "()";
}
}
}
if (fcn.self)
{
var clazz = fcn.self.constructor;
if (clazz)
{
// members
for(var key in clazz.prototype)
{
if (clazz.prototype[key] == fcn) {
return clazz.classname + ".prototype." + key + "()";
}
}
// statics
for(var key in clazz)
{
if (clazz[key] == fcn) {
return clazz.classname + "." + key + "()";
}
}
}
}
var fcnReResult = fcn.toString().match(/function\s*(\w*)\s*\(.*/);
if (fcnReResult && fcnReResult.length >= 1 && fcnReResult[1]) {
return fcnReResult[1] + "()";
}
return 'anonymous()';
},
/**
* Evaluates JavaScript code globally
*
* @lint ignoreDeprecated(eval)
*
* @param data {String} JavaScript commands
* @return {var} Result of the execution
*/
globalEval : function(data)
{
if (window.execScript) {
return window.execScript(data);
} else {
return eval.call(window, data);
}
},
/**
* Base function for creating functional closures which is used by most other methods here.
*
* *Syntax*
*
* <pre class='javascript'>var createdFunction = qx.lang.Function.create(myFunction, [options]);</pre>
*
* @param func {Function} Original function to wrap
* @param options {Map?} Map of options
* <ul>
* <li><strong>self</strong>: The object that the "this" of the function will refer to. Default is the same as the wrapper function is called.</li>
* <li><strong>args</strong>: An array of arguments that will be passed as arguments to the function when called.
* Default is no custom arguments; the function will receive the standard arguments when called.</li>
* <li><strong>delay</strong>: If set, the returned function will delay the actual execution by this amount of milliseconds and return a timer handle when called.
* Default is no delay.</li>
* <li><strong>periodical</strong>: If set the returned function will periodically perform the actual execution with this specified interval
* and return a timer handle when called. Default is no periodical execution.</li>
* <li><strong>attempt</strong>: If set to true, the returned function will try to execute and return either the results or false on error. Default is false.</li>
* </ul>
*
* @return {Function} Wrapped function
*/
create : function(func, options)
{
if (qx.core.Environment.get("qx.debug")) {
qx.core.Assert && qx.core.Assert.assertFunction(func, "Invalid parameter 'func'.");
}
// Nothing to be done when there are no options.
if (!options) {
return func;
}
// Check for at least one attribute.
if (!(options.self || options.args || options.delay != null || options.periodical != null || options.attempt)) {
return func;
}
return function(event)
{
if (qx.core.Environment.get("qx.debug"))
{
if (qx.core.Object && options.self && qx.Bootstrap.isObject(options.self) && options.self.isDisposed && qx.Bootstrap.isFunction(options.self.isDisposed))
{
if (options.self.isDisposed()) {
qx.core.Assert && qx.core.Assert.fail(
"Trying to call a bound function with a disposed object as context: " + options.self.toString() + " :: " + qx.lang.Function.getName(func)
);
}
}
}
// Convert (and copy) incoming arguments
var args = qx.lang.Array.fromArguments(arguments);
// Prepend static arguments
if (options.args) {
args = options.args.concat(args);
}
if (options.delay || options.periodical)
{
var returns = function() {
return func.apply(options.self||this, args);
};
if (qx.core.Environment.get("qx.globalErrorHandling")) {
returns = qx.event.GlobalError.observeMethod(returns);
}
if (options.delay) {
return window.setTimeout(returns, options.delay);
}
if (options.periodical) {
return window.setInterval(returns, options.periodical);
}
}
else if (options.attempt)
{
var ret = false;
try {
ret = func.apply(options.self||this, args);
} catch(ex) {}
return ret;
}
else
{
return func.apply(options.self||this, args);
}
};
},
/**
* Returns a function whose "this" is altered.
*
*
* *Native way*
*
* This is also a feature of JavaScript 1.8.5 and will be supplied
* by modern browsers. Including {@link qx.lang.normalize.Function}
* will supply a cross browser normalization of the native
* implementation. We like to encourage you to use the native function!
*
*
* *Syntax*
*
* <pre class='javascript'>qx.lang.Function.bind(myFunction, [self, [varargs...]]);</pre>
*
* *Example*
*
* <pre class='javascript'>
* function myFunction()
* {
* this.setStyle('color', 'red');
* // note that 'this' here refers to myFunction, not an element
* // we'll need to bind this function to the element we want to alter
* };
*
* var myBoundFunction = qx.lang.Function.bind(myFunction, myElement);
* myBoundFunction(); // this will make the element myElement red.
* </pre>
*
* If you find yourself using this static method a lot, you may be
* interested in the bindTo() method in the mixin qx.core.MBindTo.
*
* @see qx.core.MBindTo
*
* @param func {Function} Original function to wrap
* @param self {Object ? null} The object that the "this" of the function will refer to.
* @param varargs {arguments ? null} The arguments to pass to the function.
* @return {Function} The bound function.
*/
bind : function(func, self, varargs)
{
return this.create(func,
{
self : self,
args : arguments.length > 2 ? qx.lang.Array.fromArguments(arguments, 2) : null
});
},
/**
* Returns a function whose arguments are pre-configured.
*
* *Syntax*
*
* <pre class='javascript'>qx.lang.Function.curry(myFunction, [varargs...]);</pre>
*
* *Example*
*
* <pre class='javascript'>
* function myFunction(elem) {
* elem.setStyle('color', 'red');
* };
*
* var myBoundFunction = qx.lang.Function.curry(myFunction, myElement);
* myBoundFunction(); // this will make the element myElement red.
* </pre>
*
* @param func {Function} Original function to wrap
* @param varargs {arguments} The arguments to pass to the function.
* @return {var} The pre-configured function.
*/
curry : function(func, varargs)
{
return this.create(func, {
args : arguments.length > 1 ? qx.lang.Array.fromArguments(arguments, 1) : null
});
},
/**
* Returns a function which could be used as a listener for a native event callback.
*
* *Syntax*
*
* <pre class='javascript'>qx.lang.Function.listener(myFunction, [self, [varargs...]]);</pre>
*
* @param func {Function} Original function to wrap
* @param self {Object ? null} The object that the "this" of the function will refer to.
* @param varargs {arguments ? null} The arguments to pass to the function.
* @return {var} The bound function.
*/
listener : function(func, self, varargs)
{
if (arguments.length < 3)
{
return function(event)
{
// Directly execute, but force first parameter to be the event object.
return func.call(self||this, event||window.event);
};
}
else
{
var optargs = qx.lang.Array.fromArguments(arguments, 2);
return function(event)
{
var args = [event||window.event];
// Append static arguments
args.push.apply(args, optargs);
// Finally execute original method
func.apply(self||this, args);
};
}
},
/**
* Tries to execute the function.
*
* *Syntax*
*
* <pre class='javascript'>var result = qx.lang.Function.attempt(myFunction, [self, [varargs...]]);</pre>
*
* *Example*
*
* <pre class='javascript'>
* var myObject = {
* 'cow': 'moo!'
* };
*
* var myFunction = function()
* {
* for(var i = 0; i < arguments.length; i++) {
* if(!this[arguments[i]]) throw('doh!');
* }
* };
*
* var result = qx.lang.Function.attempt(myFunction, myObject, 'pig', 'cow'); // false
* </pre>
*
* @param func {Function} Original function to wrap
* @param self {Object ? null} The object that the "this" of the function will refer to.
* @param varargs {arguments ? null} The arguments to pass to the function.
* @return {Boolean|var} <code>false</code> if an exception is thrown, else the function's return.
*/
attempt : function(func, self, varargs)
{
return this.create(func,
{
self : self,
attempt : true,
args : arguments.length > 2 ? qx.lang.Array.fromArguments(arguments, 2) : null
})();
},
/**
* Delays the execution of a function by a specified duration.
*
* *Syntax*
*
* <pre class='javascript'>var timeoutID = qx.lang.Function.delay(myFunction, [delay, [self, [varargs...]]]);</pre>
*
* *Example*
*
* <pre class='javascript'>
* var myFunction = function(){ alert('moo! Element id is: ' + this.id); };
* //wait 50 milliseconds, then call myFunction and bind myElement to it
* qx.lang.Function.delay(myFunction, 50, myElement); // alerts: 'moo! Element id is: ... '
*
* // An anonymous function, example
* qx.lang.Function.delay(function(){ alert('one second later...'); }, 1000); //wait a second and alert
* </pre>
*
* @param func {Function} Original function to wrap
* @param delay {Integer} The duration to wait (in milliseconds).
* @param self {Object ? null} The object that the "this" of the function will refer to.
* @param varargs {arguments ? null} The arguments to pass to the function.
* @return {Integer} The JavaScript Timeout ID (useful for clearing delays).
*/
delay : function(func, delay, self, varargs)
{
return this.create(func,
{
delay : delay,
self : self,
args : arguments.length > 3 ? qx.lang.Array.fromArguments(arguments, 3) : null
})();
},
/**
* Executes a function in the specified intervals of time
*
* *Syntax*
*
* <pre class='javascript'>var intervalID = qx.lang.Function.periodical(myFunction, [period, [self, [varargs...]]]);</pre>
*
* *Example*
*
* <pre class='javascript'>
* var Site = { counter: 0 };
* var addCount = function(){ this.counter++; };
* qx.lang.Function.periodical(addCount, 1000, Site); // will add the number of seconds at the Site
* </pre>
*
* @param func {Function} Original function to wrap
* @param interval {Integer} The duration of the intervals between executions.
* @param self {Object ? null} The object that the "this" of the function will refer to.
* @param varargs {arguments ? null} The arguments to pass to the function.
* @return {Integer} The Interval ID (useful for clearing a periodical).
*/
periodical : function(func, interval, self, varargs)
{
return this.create(func,
{
periodical : interval,
self : self,
args : arguments.length > 3 ? qx.lang.Array.fromArguments(arguments, 3) : null
})();
}
}
});