///////////////////////////////////////////////////////////
// Ajax - RPC-Klasse
//
// Wrapper-Klasse für RPC-Kommunikation via XMLHTTPRequest.
//
// Copyright (c) 2008 Jannes Philipp Mönnighoff
// Hamburg, Germany
//
// Projekt:        Ajax
// Version:        1.0
// Erstellt:       2008-05-11
// Geändert:       2008-05-20
///////////////////////////////////////////////////////////

function Ajax(requestMethod, url, callbackFunction) {
	/**
	 * Parameter einlesen.
	 * @var	 
	 */
	this.requestMethod = requestMethod;
	this.url = url;
	this.callbackFunction = callbackFunction;
	
	/**
	 * Zähler für aktive Anfragen.
	 * @var int	 
	 */
	this.pendingCalls = 0;

	/**
	 * Maximale Anzahl paralleler Anfragen.
	 * @var int
	 */
	this.maxPendingCalls = 10;

	/**
	 * Debug-Flag. "True" für Debugmodus.
	 * @var bool	 
	 */
	this.isDebug = false;

	///////////////////////////////////////////////////////////
	// PRIVATE METHODEN
	///////////////////////////////////////////////////////////
	
	/**
	 * Gibt im Debugmodus Debugnachrichten aus.
	 * @param string message 
	 */
	this._debug = function(message) {
		if(this.isDebug)
			alert("Debug-Nachricht: \n\n" + message);
		}
	
	/**
	 * Gibt im Debugmodus Errornachrichten aus.
	 * @param string message 
	 */
	this._error = function(message) {
		if(this.isDebug)
			alert("ERROR-Nachricht: \n\n" + message);
		}
	
	/**
	 * Liefert ein XMLHTTPRequest-Objekt (Browser-spezifisch).
	 * @return xmlHttpRequest
	 */
	this._createReqObject = function() {
		try {
		  // Firefox, Opera 8.0+, Safari
		   return new XMLHttpRequest();
		   }
		catch (e) {
		  // Internet Explorer
		  try {
		     return new ActiveXObject("Msxml2.XMLHTTP");
		    }
		  catch (e) {
		    try {
		      return new ActiveXObject("Microsoft.XMLHTTP");
		      }
		    catch (e)
		      {
	    		this._error("Browserproblem: Konnnte kein XMLHTTPRequest-Objekt erstellen.");
		      return null;
		      }
		    }
		  }
		}
		
	/**
	 * Standard-Callbackfunktion, ruft die gewünschte indiv.
	 * Callbackfunktion auf.
	 * @param Object data Enthält Angaben zum Aufruf:
	 * - xmlHttpRequest reqObject 
	 * - string callContext
	 */
	this._callback = function(data) {
		if(data.reqObj.readyState == 4) {
			this.pendingCalls --;
			if(data.reqObj.status == 200) {
				// Debug-Bericht:
				this._debug(
					"Antwort vom Server erhalten (Status==200)." +
					"\ncallContext: " + data.callContext +
					"\npendingCalls: " + this.pendingCalls
					);
					
				// Datenobjekt für externe Callback-Funktion:
				var responseData = {
					text: data.reqObj.responseText,
					xml: data.reqObj.responseXML,
					callContext: data.callContext
					};
				
				// Externe Callback-Funktion aufrufen:
				this.callbackFunction(responseData);
				}
			else {
				// Error-Meldung:
				this._error(
					"Server-Fehler: " + data.reqObj.status + " " + data.reqObj.statusText +
					"\ncallContext: " + data.callContext
					);
				}
			}
		}
		
	/**
	 * Wandelt ein Objekt in einen URL-String um.
	 * @param object data
	 * @return string Nach dem Muster "key1=value1&key2=value2".
	 */
	this._dataUrlEncode = function(data) {
		var str = "";
		for(key in data) {
			str += "&" + key + "=" + encodeURIComponent(data[key]);
			}
		str = str.substr(1);
		return str;
		}
	
	/**
	 * Liefert den Inhalt der Felder eines Formulars als URL-String.
	 * @param string formName
	 * @return string  
	 */
	this._formDataUrlEncode = function(formName) {
		var form = document.forms[formName];
		if(form) {
			var str = "";
			for(i=0; i<form.elements.length; i++) {
				str += "&" + form.elements[i].name + "=" + encodeURIComponent(form.elements[i].value);
				}
			str = str.substr(1);
			return str;
			}
		else
			this._error("Das Formular " + formName + " existiert nicht. Daten konnten nicht geladen werden.");
			return false;
		}
		
	///////////////////////////////////////////////////////////
	// PUBLIC METHODEN
	///////////////////////////////////////////////////////////
	
	/**
	 * Führt eine Anfrage mit Daten aus einem Objekt aus.
	 * @param object object Enthält die zu sendenden Daten.
	 * @param string callContext
	 */
	this.objectCall = function(object, callContext) {
		this.call(this._dataUrlEncode(object), callContext);
		}
	
	/**
	 * Führt eine Anfrage mit Daten aus einem Formular aus.
	 * @param string formName Name des Formulars
	 * @param string callContext
	 */
	this.formCall = function(formName, callContext) {
		var data = this._formDataUrlEncode(formName);
		if(data)
			this.call(data, callContext);
		}
	
	/**
	 * Führt eine Anfrage aus.
	 * @param string[] data Zu sendende Daten, URL-enkodiert
	 * @param string callContext Zur Indentifizierung der
	 * Anfrage in der Callback-Funktion.
	 */
	this.call = function(reqData, callContext) {
		if(this.pendingCalls < this.maxPendingCalls) {
			this._debug(
				"Die call-Methode wurde mit folgenden Daten aufgerufen:" +
				"\n- reqData: " + reqData +
				"\n- callContext: " + callContext +
				"\n- pendingCalls: " + this.pendingCalls
				);
	
			// Zähler erhöhen:
			this.pendingCalls ++;
			
			// Referenz auf aktuelles Objekt:
			var caller = this;
			
			// Neues XMLHTTPRequest-Objekt für aktuelle Anfrage:
			var reqObj = this._createReqObject();
	
			// Datenobjekt für interne Callback-Funktion:
			var callbackData  = {
				reqObj: reqObj,
				callContext: callContext
				};
	
			// Lokale Callback-Funktion mit Parametern übergeben:
			reqObj.onreadystatechange = function() {
				caller._callback(callbackData);
				}
			
			// Ja nach Request-Methode, Anfrage erstellen und senden:
			switch(this.requestMethod) {
				case "POST":
					reqObj.open(this.requestMethod, this.url, true);
					reqObj.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
					reqObj.send(reqData);
					break;
				case "GET":
					reqObj.open(this.requestMethod, this.url + "?" + reqData, true);
					reqObj.send(null);
					break;
				default:
					this._error("Unbekannte Request-Methode: " + this.requestMethod);
				}
			}
		else {
			this._error(
				"Zuviele laufende Anfragen." +
				"\npendingCalls: " + this.pendingCalls +
				"\nmaxPendingCalls: " + this.maxPendingCalls
				);
			}
		}
		
	}
