Original Post
I'm developing a robust function "concatenation" system for javascript. This will be handy for large, dynamic web-apps developed by multiple people. Specifically, it's intended to prevent one developer from wiping out another developer's event handlers, like document.onmousedown, document.onmousemove etc. And, an example to go along with it: It should theoretically concat any two functions. It's ideal for parameterless functions that do not return a value, though it can still handle parameters and return values. In the concat method, you might see that I recreate the two functions to concat based off of their string body. This is to maintain scope exclusion between the two functions. Prefixing the parameter names in the parameter list makes it possible to avoid collision between parameter names. If either of the two functions return a value, then the concatenated function returns an array, the first element being the return value of the first function, similar for the second element. For the most part, it will work for my purposes. But the handling of return values is a little sketchy. Any ideas? edit: what is with wrapping numbers with square brackets in the source tag? [Edited by - capn_midnight on July 18, 2006 9:09:00 AM]
//remove trailing spaces from a string
String.prototype.trim = function()
{
var start = -[1], end = -[1];
for(var i = [0]; i < this.length; ++i)
{
if(start == -[1] && this.charAt(i) != " ")
{
start = i;
}
if(end == -[1] && this.charAt(this.length - i) != " ")
{
end = i;
}
}
return this.substring(start, end + [1]);
}
//the name, sans parens, parameter list, and function body
Function.prototype.getName = function()
{
var sFunc = this.toString();
var start = sFunc.indexOf(" ") + [1];
var end = sFunc.indexOf("(");
return sFunc.substring(start, end);
}
//the body of the function, sans controlling braces.
Function.prototype.getBody = function()
{
var sFunc = this.toString();
var start = sFunc.indexOf("{") + [1];
var end = sFunc.lastIndexOf("}");
return sFunc.substring(start, end);
}
//a comma delimited list of the names of parameters this function recieves
Function.prototype.getParameterList = function()
{
var sFunc = this.toString();
var start = sFunc.indexOf("(") + [1];
var end = sFunc.indexOf(")");
return sFunc.substring(start, end);
}
//the parameter list, with each parameter name prefixed with the function name (important for resolving conflicts between parameter names)
Function.prototype.getPrefixedParameterList = function()
{
var params = this.getParameterList();
var newParams = "";
if(params.length > [0])
{
var arr = params.split(",");
var prefix = this.getName() + "_";
for(var i = [0]; i < arr.length; ++i)
{
newParams += prefix + arr.trim();
if(i < arr.length - [1])
{
newParams += ",";
}
}
}
return newParams;
}
//the meat
Function.prototype.concat = function(func)
{
if(func != null)
{
var partA = "var a = function("+this.getParameterList()+"){"+this.getBody()+"};\nvar retA = a("+this.getPrefixedParameterList()+");";
var partB = "var b = function("+func.getParameterList()+"){"+func.getBody()+"};\nvar retB = b("+func.getPrefixedParameterList()+");";
var partC = "var arr = new Array();\nif(retA != undefined){arr[arr[arr.length]]=retA;}\nif(retB != undefined){arr[arr[arr.length]]=retB;}\nif(arr.length > [0]){return arr;}";
var body = partA + "\n" + partB + "\n" + partC;
partA = this.getPrefixedParameterList();
partB = func.getPrefixedParameterList();
var params = partA;
if(params.length > [0])
{
params += ",";
}
params += partB;
return new Function(params, body);
}
return this;
}
function func0()
{
document.writeln("wtf, mate");
}
function func1(p1)
{
document.writeln(p1);
}
function func2(p1, p2)
{
document.writeln(p1, p2);
}
function func3(p1, p2, p3)
{
document.writeln(p1, p2, p3);
}
func0();
func1("a");
func2("b", "c");
func3("d", "e", "f");
document.writeln();
var func4 = func0.concat(func1);
func4("g");
document.writeln();
var func4 = func4.concat(func2);
func4("h", "i", "j");
document.writeln();
func4 = func4.concat(func3);
func4("k", "l", "m", "n", "o", "p");
document.writeln(func4.toString());