/**
 * 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';
	
	// 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;
	
	/*** Private methods ***/
	
	/**
	 * Return instance of XMLHttpRequest class
	 *
	 * @param[in] params Parameters of function or method which has been called
	 * @return Object of XMLHttpRequest
	**/
	var _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( "application/json" );
		}
		
		return xmlHttpRequest;
	};

	/**
	 * Return callback function from params
	 *
	 * @param[in] params Parameters of function or method which has been called
	 * @return Callback function
	**/
	var _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
	**/
	var _prepareParams = function( params )
	{
		var preparedParams = [];

		if ( typeof( params ) === "object" && params.length > 0 )
		{
			var end = _getCallbackFunction( params ) !== null ? 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
	**/
	var _prepareData = function( func, params )
	{
		var data = { func:func, params:_prepareParams( params ) };
		
		return "ajax=" + encodeURIComponent( JSON.stringify( data ) );
	};
	
	var _parseResponse = function( xmlHttpRequest )
	{
		// Aborted
		if ( xmlHttpRequest.status === 0 )
		{
			return;
		}
		
		// Ignore error handler output
		if ( self.call_params[0] === self.error_handler )
		{
			self.response = {};
			return;
		}
		
		try {
			// Dekodieren
			try {
				if ( xmlHttpRequest.status!==200 )
				{
					throw "Status code:" + xmlHttpRequest.status;
				}
				self.response = JSON.parse( xmlHttpRequest.responseText );
			}
			catch (e) 
			{
				if ( xmlHttpRequest.responseText )
				{
					throw xmlHttpRequest.responseText;
				}
				
				throw e.description ? e.description : e;
			}
		}
		catch (exception)
		{
			if ( typeof self.onError !== 'undefined' )
			{
				self.onError( exception, self.call_params, xmlHttpRequest );
			}
			
			return;
		}
		
		if ( self.response.exec )
		{
			eval( self.response.exec );
			return;
		}
	};
	
	/*** 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 && xmlHttpRequest.status !== 0 )
				{
					_parseResponse( xmlHttpRequest );
					callbackFunction( self.response.result );
				}
			};
		}
		
		try {
			xmlHttpRequest.send(data);
		}
		catch (e) {
			return null;
		}
		
		if ( callbackFunction === null  )
		{
			_parseResponse( xmlHttpRequest );
			return self.response.result;
		}
		
		return null;
	};
	
	this.onError = function( exception, call_params, xmlHttpRequest )
	{
		if ( self.error_handler )
		{
			self.call( self.error_handler, [exception, call_params, xmlHttpRequest] );
		}
	};
	
	this.replaceSync = function( id, func, params )
	{
		var parameters = [];
		
		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 = [];
		
		for (var i = 2; i < this.replaceAsync.arguments.length; ++i)
		{
			parameters.push( this.replaceAsync.arguments[i] );
		}
		
		this.call( func, parameters, function( content ) { Ajax.replace( id, content ); } );
	};
}

/**
 * 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 = {};
	
	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;
				default:
					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] = [];
				}
				
				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;
};

