/**	generate a JavaScript literal version of an object.

	@param value	the thing that should be converted to a literal value
	@returns a string representation of the value which is valid JavaScript
 **/
function toLiteral( value )
{
	if (null===value)
		return "null";
	
	if (undefined===value)
		return "undefined";
 	
 	if (value instanceof Function)
	{
		var info= functionInfo( value );
		var args= info.argumentNames.join(", ");
		if (args)
			args= " " + args + " ";
		var str= "function " + info.name + "(" + args + ") {...}";
		return str;
	}
	
	var type= typeof(value);
	
	if ('string' == type)
	{
		return '"' + value + '"';
	}
	
	if ('number' == type ||
		'boolean' == type)
	{
		return value.toString();
	}

	if (value instanceof Array)
	{
		return dumpArray( value );
	}
	
	if (value instanceof Object)
	{
		return dumpObject( value );
	}
	
	return value;
}

/**	Create a string version of an object.
	@param obj	the object to translate into a string
	@returns a JavaScript literal representation of the object
 **/
function dumpObject( obj )
{
	var p;
	var s= "";
	var value;
	
	for (p in obj)
	{
		value= obj[p];

		if ('function' == typeof(value))
			continue;
			
		if (s)
			s+= ", ";

		s+= p + ": " + toLiteral(value);
	}
	
	if (s)
		return "{ " + s + " }";
	else
		return "{}";
}

/**	Create a literal string version of an array.
	@param array	the array object to translate into a string
	@returns a JavaScript literal representation of the array
 **/
function dumpArray( array )
{
	var s= "";
	var value;
	var index;
	
	for (index=0; index<array.length; ++index)
	{
		if (s)
			s+= ", ";
			
		value= array[index];
		s+= toLiteral(value);
	}
	
	if (s)
		return "[ " + s + " ]";
	else
		return "[]";
}

/**	Declare a regular expression that will match the name and arguments of a
	function declaration.
 **/
var functionInfoRegex= /function\s*(\w*)?\s*\(([^\)]*)\)/;
var nativeFunctionRegex= /(native code)|(Internal Function)/i;

/**	Pull out the name and arguments of a function.
	@param fn	the function
	@returns an object containing the name and argument names of the function
 **/
function functionInfo( fn )
{
	var callingFnText= fn.toString();

	var matches= callingFnText.match( functionInfoRegex );
	if (!matches)
		return { name: "<<internal function>>", argumentNames: [] };
	
	return	{
				name: matches[1],
				argumentNames: matches[2].replace( /\s*/g,"").split(",")
			};
}

/** Trace helper
 **/
function trace( string )
{
    var callingFn= trace.caller;
    var callingFnName;
	var node;
	
	if (!string)
	{
		print();
		return;
	}
	
    if (callingFn)
    	callingFnName= functionInfo( callingFn ).name;
    else
    	callingFnName= "global";

	var indent= "\n" + " ".repeat( callingFnName.length + 2 );
	var str= string.replace( /\n/g, indent );

	print( callingFnName + ": " + str );	
}

function print( string )
{
	var traceElement= document.getElementById( "trace" );
    node= document.createTextNode( string + "\n" );
    traceElement.appendChild( node );
}

String.prototype.repeat= function( count )
{
	var result="";
	var str= this.toString();
	
	while (count--)
		result+= str;
	return result;
}

/**	Display all arguments for a function.
 **/
function traceArgs( args )
{
	if (!args)
		args= arguments.caller.arguments;
	
	var callerInfo= functionInfo( args.callee );
	var str= callerInfo.name + "( ";
	var indent= " ".repeat( str.length );
	var index;
	var argCount= callerInfo.argumentNames.length;
	
	if (argCount < args.length)
		argCount= args.length;

	for (index=0; index<argCount; ++index)
	{
		if (index)
			str+= ",\n" + indent;
		if (callerInfo.argumentNames.length>index)
			str+= callerInfo.argumentNames[index] + " = ";
		str+= toLiteral(args[index]);
	}
	
	trace( str + " )" );
}

function copy_clip(meintext)
{
 if (window.clipboardData) 
   {
   
   // the IE-manier
   window.clipboardData.setData("Text", meintext);
   
   // waarschijnlijk niet de beste manier om Moz/NS te detecteren;
   // het is mij echter onbekend vanaf welke versie dit precies werkt:
   }
   else if (window.netscape) 
   { 
   
   // dit is belangrijk maar staat nergens duidelijk vermeld:
   // you have to sign the code to enable this, or see notes below 
   netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
   
   // maak een interface naar het clipboard
   var clip = Components.classes['@mozilla.org/widget/clipboard;1']
                 .createInstance(Components.interfaces.nsIClipboard);
   if (!clip) return;
   
   // maak een transferable
   var trans = Components.classes['@mozilla.org/widget/transferable;1']
                  .createInstance(Components.interfaces.nsITransferable);
   if (!trans) return;
   
   // specificeer wat voor soort data we op willen halen; text in dit geval
   trans.addDataFlavor('text/unicode');
   
   // om de data uit de transferable te halen hebben we 2 nieuwe objecten 
   // nodig om het in op te slaan
   var str = new Object();
   var len = new Object();
   
   var str = Components.classes["@mozilla.org/supports-string;1"]
                .createInstance(Components.interfaces.nsISupportsString);
   
   var copytext=meintext;
   
   str.data=copytext;
   
   trans.setTransferData("text/unicode",str,copytext.length*2);
   
   var clipid=Components.interfaces.nsIClipboard;
   
   if (!clip) return false;
   
   clip.setData(trans,null,clipid.kGlobalClipboard);
   
   }
   alert("Following info was copied to your clipboard:\n\n" + meintext);
   return false;
}
