/**
 * Ajax JavaScript class
 *
 * Contains methods to create the XMLHttpRequest object and to call remote
 * functions and object methods.
 *
**/

function Ajax( handler, additional_request_vars )
{
	/*** Public vars ***/

	// Wartegrafik, die beim Aufruf von replaceAsync ausgegeben wird (wenn leer -> keine Grafik)
	this.async_image		= '/gfx/wait.gif';
	
	// Fehler anzeigen
	this.show_errors		= false;
	
	// Serverseitiger Errorhandler
	this.error_handler		= 'Ajax::handleError';
	
	// Die Aufrufparameter von call()
	this.call_params		= [];
	
	// Response-Objekt des letzten Aufrufs
	this.response			= false;
	
	// Self für private Methoden zuweisen
	var self = this;
	
	/*** Public methods ***/
	
	/**
	 * Call remote function
	 *
	 * @param[in] func Name of remote function
	 * @param[in] params Parameters of local function which has been called
	**/
	this.call = function( func, params, callback )
	{
		if ( typeof( callback ) != 'undefined' )
			params.push( callback );
		
		this.call_params	= [func, params];
		
		var data			= _prepareData( func, params ) + ( additional_request_vars ? '&' + additional_request_vars : '' );
		var xmlHttpRequest	= _getXmlHttpRequestObject( params );
		var callbackFunction = _getCallbackFunction( params );
		
		xmlHttpRequest.open( 'POST', handler, callbackFunction != null );
		
		xmlHttpRequest.setRequestHeader( "Method",		  "POST " + handler + " HTTP/1.1" );
		xmlHttpRequest.setRequestHeader( "Content-Type",  "application/x-www-form-urlencoded" );
		
		if ( callbackFunction != null )
		{
			xmlHttpRequest.onreadystatechange = function()
			{
				if ( xmlHttpRequest.readyState == 4 )
				{
					_parseResponse( xmlHttpRequest );
					callbackFunction( self.response.result );
				}
			}
		}
		
		try {
			xmlHttpRequest.send(data);
		}
		catch (e) {
			return;
		}
		
		if ( !_paramsHaveCallbackFunction( params ) )
		{
			_parseResponse( xmlHttpRequest );
			return self.response.result;
		}
		
		return null;
	}
	
	/**
	 * Ruft die Funktion mit den übergebenen Parametern auf
	 */
	this.callFunc = function()
	{
		var func = this.callFunc.arguments[0];
		
		var args = new Array();
		for ( i = 1; i < this.callFunc.arguments.length; ++i )
			args.push( this.callFunc.arguments[i] );
		
		return this.call( func, args );
	}
	
	this.replaceSync = function( id, func, params )
	{
		var parameters = new Array();

		for (var i = 2; i < this.replaceSync.arguments.length; ++i)
			parameters.push( this.replaceSync.arguments[i] );

		Ajax.replace( id, this.call( func, parameters ) );
	}

	this.replaceAsync = function( id, func, params )
	{
		if ( this.async_image )
			Ajax.replace( id, '<img src="' + this.async_image + '">' );

		var parameters = new Array();
		
		for (var i = 2; i < this.replaceAsync.arguments.length; ++i)
			parameters.push( this.replaceAsync.arguments[i] );

		parameters.push( function( content ) { Ajax.replace( id, content ); } );

		this.call( func, parameters );
	}
	
	/*** Private methods ***/
	
	/**
	 * Return instance of XMLHttpRequest class
	 *
	 * @param[in] params Parameters of function or method which has been called
	 * @return Object of XMLHttpRequest
	**/
	_getXmlHttpRequestObject = function( params )
	{
		var xmlHttpRequest = false;

		if ( !window.XMLHttpRequest ) {
			window.XMLHttpRequest = function() { return new ActiveXObject('Microsoft.XMLHTTP') };
		}
		
		xmlHttpRequest = new XMLHttpRequest();
		
		// Override mime type if browser supports it (like Mozilla)
		if ( xmlHttpRequest.overrideMimeType )
			xmlHttpRequest.overrideMimeType( "text/json" );
		
		return xmlHttpRequest;
	}
	
	/**
	 * Do params include callback function?
	 *
	 * @param[in] params Parameters of function or method which has been called
	 * @return Returns true if params contain callback function
	**/
	_paramsHaveCallbackFunction = function( params )
	{
		return _getCallbackFunction( params ) != null;
	}

	/**
	 * Return callback function from params
	 *
	 * @param[in] params Parameters of function or method which has been called
	 * @return Callback function
	**/
	_getCallbackFunction = function( params )
	{
		if ( typeof( params ) != "object" || params.length == 0 )
			return null;
		
		var last_param = params[ params.length - 1 ];
		
		return typeof( last_param ) == "function" ? last_param : null;
	}

	/**
	 * Prepare parameters
	 *
	 * @param[in] params Parameters of function or method which has been called
	 * @return Prepared parameters
	**/
	_prepareParams = function( params )
	{
		var preparedParams = new Array();

		if ( typeof( params ) == "object" && params.length > 0 )
		{
			var end = _paramsHaveCallbackFunction( params ) ? 1 : 0;

			for ( var i = 0; i < params.length - end ; i++ )
				preparedParams.push( params[ i ] );

		}

		return preparedParams;
	}

	/**
	 * Prepare data
	 *
	 * @param[in] func
	 * @param[in] params Parameters of function or method which has been called
	 * @return Prepared data
	**/
	_prepareData = function( func, params )
	{
		var data = { func:func, params:_prepareParams( params ) };
		
		return "ajax=" + encodeURIComponent( JSON.stringify( data ) );
	}
	
	_parseResponse = function( xmlHttpRequest )
	{
		try {
			
			// Dekodieren
			try {
				self.response = JSON.parse( xmlHttpRequest.responseText );
			}
			catch (e) {
				throw 'Decodieren fehlgeschlagen! (' + e.description + ')';
			}
 			
			if ( !self.response )
			{
				if ( xmlHttpRequest.responseText )
					throw xmlHttpRequest.responseText;
				
				return;
			}
		}
		catch (exception) {
				
			if ( self.call_params[0] == self.error_handler )
				return;
			
			if ( self.show_errors )
			{
				var div = document.createElement('div');
				div.setAttribute('style', 'border:3px solid red; position:absolute; top:0px; left:0px; margin:8px; z-index:20; background:white;' );
				
				var div2 = document.createElement('div');
				div2.setAttribute('style', 'background:red; font-weight:bold; color:white; font-size:12px; font-family:sans-serif; cursor:pointer;')
				div2.onclick = function() { this.parentNode.style.display = 'none'; };
				text = document.createTextNode('X  Fehlerhafte Rückgabe');
				div2.appendChild( text );
				div.appendChild( div2 );
				
				var div3 = document.createElement('div');
				div3.setAttribute('style', 'padding:3px;' );
				div3.innerHTML = exception;
				div.appendChild( div3 );
				
				document.getElementsByTagName('body')[0].appendChild( div );
			}
			
			if ( self.error_handler )
			{
				self.call( self.error_handler, [self.call_params, exception, xmlHttpRequest, function(){}] );
			}
			
			return;
		}
		
		if ( self.response.exec )
		{
			eval( self.response.exec );
			return;
		}
	}
}

/**
 * holt alle Elemente eines Formulars und gibt sie in einem assoziativen Array zurück
 *
 * @param[in]	Formularname oder Formular-Object
 * @return		Object (assoz. Array)
**/

Ajax.getFormData = function( form )
{
	if ( typeof form == 'string' )
		form = document.getElementById( form );
	
	if ( !form )
		return null;
	
	var parameters = new Object();

	for( i=0 ; i < form.elements.length ; i++) {

		element = form.elements[i];

		if ( element.name ) {

			var value = element.value;

			switch ( element.type ) {
			case 'checkbox':
				if ( !element.checked )
					continue;
				break;

			case 'radio':
				if ( !element.checked )
					continue;
				break;
			}

			// Sonderbehandlung für Formularfelder für nicht-assoziative Arrays (z.B. 'var[]')
			if ( element.name.match(/\[\]$/) ) {
				var name = element.name.replace(/\[\]$/, '');
				if ( ! parameters[ name ] )
					parameters[name] = new Array();

				if ( element.options ) {
					for (var j = 0; j < element.options.length; j++)
						if (element.options[j].selected)
							parameters[name].push( element.options[ j ].value );
				}
				else
					parameters[name].push( value );
			}
			else
				parameters[ element.name ] = value;

		}
	}

	return parameters;
}

Ajax.replace = function( id, content )
{
	var tag = document.getElementById(id);
	if ( !tag )
		throw "Tag with id '" + id + "' not found!";
	
	tag.innerHTML = content;
}
