if ( typeof AsyncRequest == 'undefined' )
	var AsyncRequest = function() {
	};

//here some static variables
AsyncRequest.bShowProgressIndicator = true;
AsyncRequest.bGlobalProgressIndicator = true;
AsyncRequest._iReqCount = 0;
AsyncRequest._iPIndiCount = 0;
AsyncRequest.preloaderImageSrc = "frontend/img/progress.gif";
AsyncRequest.preloaderWidth = 120;
AsyncRequest.preloaderHeight = 40;
AsyncRequest.preloaderImageWidth = 16;
AsyncRequest.preloaderImageHeight = 16;
AsyncRequest.preloaderImage = document.createElement('img');
AsyncRequest.preloaderImage.onError = function() {AsyncRequest.bShowProgressIndicator = false;};

/**
 * Sends a HTML form asyncounously using the POST method
 */
AsyncRequest.sendForm = function(sContr, sFunc, oForm, aTarget, aCallbackFunc, hideProgressIndicator) {
	AsyncRequest.sndReq(
		'gate.php?c=' + sContr + '&f=' + sFunc + '&async_call=1',
		'POST',
		AsyncRequest.getFormData(oForm),
		aTarget,
		aCallbackFunc,
		hideProgressIndicator
	);
};

/**
 * Sends HTML form elements in a block element asyncounously using the POST method
 */
AsyncRequest.sendSubForm = function(sContr, sFunc, oContainer, aTarget, aCallbackFunc, hideProgressIndicator) {
	AsyncRequest.sndReq(
		'gate.php?c=' + sContr + '&f=' + sFunc + '&async_call=1',
		'POST',
		AsyncRequest.getPartiallyFormData(oContainer),
		aTarget,
		aCallbackFunc,
		hideProgressIndicator
	);
};

/**
 * Send an asyncronous request without parameters
 */
AsyncRequest.send = function(sContr, sFunc, aTarget, aCallbackFunc, hideProgressIndicator) {
	AsyncRequest.sndReq(
		'gate.php?c=' + sContr + '&f=' + sFunc + '&async_call=1',
		'GET',
		null,
		aTarget,
		aCallbackFunc,
		hideProgressIndicator
	);
};

/**
 * Sends an asyncronous request using the GET method
 */
AsyncRequest.sendGet = function(sContr, sFunc, aTarget, sParams, aCallbackFunc, hideProgressIndicator) {
	AsyncRequest.sndReq(
		'gate.php?c=' + sContr + '&f=' + sFunc + '&async_call=1' + sParams,
		'GET',
		null,
		aTarget,
		aCallbackFunc,
		hideProgressIndicator
	);
};

/**
 * Sends an asyncronous request to the given destination address
 * @param sDestAddr string The destination address including GET parameters
 * @param sMethod string The method used to send the request (POST | GET)
 * @param sData string A string of data used to send POST data
 * @param aTarget array|object The destination for results
 * @param aCallbackFunc array|function The callback function(s)
 * @param hideProgressIndicator boolean Indicates that the progress indicator should be hidden
 */
AsyncRequest.sndReq = function(sDestAddr, sMethod, sData, aTarget, aCallbackFunc, hideProgressIndicator) {
    var oReqestObject = AsyncRequest.createRequestObject();
    oReqestObject.open(sMethod, sDestAddr, true);
	var iRequestId = ++AsyncRequest._iReqCount;
	//copy this value, so we can change it for the next request if we want
	var bGlobalPI = AsyncRequest.bGlobalProgressIndicator || (aTarget == undefined);
    oReqestObject.onreadystatechange = function() {
        AsyncRequest.handleResponse(oReqestObject, iRequestId, aTarget, aCallbackFunc, bGlobalPI, hideProgressIndicator);
    }
    if (sMethod == "POST") {
        oReqestObject.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8');
        oReqestObject.setRequestHeader('Content-length', sData.length );
        oReqestObject.send(sData);
    } else {
        oReqestObject.send(null);
	}
};

/**
 *
 */
AsyncRequest.handleResponse = function(oRequestObject, iRequestId, aTarget, aCallbackFunc, bGlobalPI, hideProgressIndicator) {
    switch(oRequestObject.readyState) {
		case 1:
			if ((hideProgressIndicator == undefined) || (!hideProgressIndicator))
				AsyncRequest.showProgressIndicator(aTarget, iRequestId, bGlobalPI);
			break;
		case 4:
			AsyncRequest.fillElements(aTarget, oRequestObject.responseText);
			if ((hideProgressIndicator == undefined) || (!hideProgressIndicator))
				AsyncRequest.hideProgressIndicator(aTarget, iRequestId, bGlobalPI);
			AsyncRequest.callCallbackFunctions(oRequestObject, aCallbackFunc);
			break;
		default:
			//do nothing at this time
			break;
    }
	//window.document.title = "Indicators: " + AsyncRequest._iPIndiCount.toString() + "   Request-ID: " +AsyncRequest._iReqCount.toString();
};

/**
 * Fills multiple elements using the result of the asynchronous request
 */
AsyncRequest.fillElements = function(aTarget, sValue) {
	if (aTarget) {
		if (aTarget instanceof Array) {
			for (var i = 0; i < aTarget.length; i++)
				AsyncRequest.fillElement(aTarget[i], sValue);
		} else {
			AsyncRequest.fillElement(aTarget, sValue);
		}
	}
};

/**
 * Fills a single element using the result of the asynchronous request
 */
AsyncRequest.fillElement = function(oTarget, sValue) {
	switch(oTarget.nodeName) {
		//case 'select-multiple':
		//case 'select-one':
		case 'SELECT':
			AsyncRequest.updateIESelect(oTarget, sValue);
			break;
		case 'TR':
			AsyncRequest.updateTableRow(oTarget, sValue);
			break;
		default:
			oTarget.innerHTML = sValue;
			break;
	}
}

/**
 * Calls the callback functions and gives them the result of the request as first parameter
 */
AsyncRequest.callCallbackFunctions = function(oRequestObject, aCallbackFunc) {
	if (aCallbackFunc)
		if (aCallbackFunc instanceof Array) {
			for (var i = 0; i < aCallbackFunc.length; i++)
				aCallbackFunc[i](oRequestObject.responseText);
		} else {
			aCallbackFunc(oRequestObject.responseText);
		}
};

/**
 * Tries to generate a unique id for the given element
 */
AsyncRequest.uniqueId = function(oElement) {
	var sResult = '';
	if (oElement.name)
		sResult += oElement.name;
	if (oElement.id)
		sResult += oElement.id;
	return sResult;
}

/**
 *
 */
AsyncRequest.showProgressIndicator = function(aTarget, iRequestId, bGlobalPI) {
	if (!AsyncRequest.bShowProgressIndicator)
		return;
	//increment process indicator counter
	if (bGlobalPI) {
		AsyncRequest._iPIndiCount++;
		if (AsyncRequest._iPIndiCount == 1)
			AsyncRequest.createProgressIndicator(null, '_pIndi', bGlobalPI);
	}
	else
	{
		if (aTarget instanceof Array) {
			for (var i = 0; i < aTarget.length; i++) {
				if (aTarget[i] == undefined) continue;
				AsyncRequest.createProgressIndicator(aTarget[i], '_pIndi_' + AsyncRequest.uniqueId(aTarget[i]) + iRequestId.toString(), bGlobalPI);
			}	
		} else {
			if (aTarget == undefined) return;
			AsyncRequest.createProgressIndicator(aTarget, '_pIndi' + iRequestId.toString(), bGlobalPI);
		}
	}
};

/**
 *
 */
AsyncRequest.createProgressIndicator = function(oTarget, sId, bGlobalPI) {
	if (!AsyncRequest.bShowProgressIndicator)
		return;

	if (AsyncRequest.preloaderImage.src == '')
		AsyncRequest.preloaderImage.src = AsyncRequest.preloaderImageSrc;

	var pTop = 0;
	var pLeft = 0;
	if (!bGlobalPI) {
		pTop = (oTarget.offsetHeight ? oTarget.offsetHeight : 0) / 2 - AsyncRequest.preloaderHeight / 2;
		pLeft = (oTarget.offsetWidth ? oTarget.offsetWidth : 0) / 2 - AsyncRequest.preloaderWidth / 2;
		var aPos = Utils.getPosition(oTarget);
		pLeft += aPos[0];
		pTop += aPos[1];
	} else {
		var innerSize = Utils.getInnerSize();
		pTop = innerSize[1] / 2 - AsyncRequest.preloaderHeight / 2;
		pLeft = innerSize[0] / 2 - AsyncRequest.preloaderWidth / 2;
	}
	
	pTop = pTop.toFixed(0);
	pLeft = pLeft.toFixed(0);
	
	var img = AsyncRequest.preloaderImage.cloneNode(true);// document.createElement('img');
	img.alt = 'Ladevorgang ...';
	img.id = 'img' + sId;
	img.style.display = 'block';
	img.style.margin = '0 auto';
	img.style.width = AsyncRequest.preloaderImageWidth;
	img.style.height = AsyncRequest.preloaderImageHeight;
	
	var text = document.createTextNode('Bitte warten...');
	var br = document.createElement('br');
	var div =  document.createElement('div');
	div.setAttribute("id", sId);
	
	div.appendChild(img);
	div.appendChild(br);
	div.appendChild(text);
	div.appendChild(br);

	div.style.fontFamily = 'Verdana';
	div.style.display = 'block';
	div.style.position = 'absolute';
	div.style.paddingTop = '10px';
	div.style.zIndex = '102';
	div.style.visibility = 'visible';
	div.style.textAlign = 'center';
	div.style.backgroundColor = 'rgb(255,255,255)';
	div.style.borderWidth = '2px';
	div.style.borderStyle = 'solid';
	div.style.borderColor = "#AAAAAA";
	div.style.fontSize = '12px';
	div.style.fontWeight = 'bold';
	div.style.width = AsyncRequest.preloaderWidth.toString() + "px";
	div.style.height = AsyncRequest.preloaderHeight.toString() + "px";
	div.style.left = pLeft.toString() + "px";
	div.style.top = pTop.toString() + "px";
	
	if (window.document.body != undefined)
		window.document.body.appendChild(div);
	else
		document.getElementsByTagName('body')[0].appendChild(div);
};

/**
 * Hides the progress indicator
 */
AsyncRequest.hideProgressIndicator = function(aTarget, iRequestId, bGlobalPI) {
	if (!AsyncRequest.bShowProgressIndicator)
		return;
	if (bGlobalPI) {
		//decrement process indicator counter
		if (--AsyncRequest._iPIndiCount == 0)
			AsyncRequest.destroyProgressIndicator('_pIndi');
	}
	else
		if (aTarget instanceof Array) {
			for (var i = 0; i < aTarget.length; i++) {
				if (aTarget[i] == undefined) continue;
				AsyncRequest.destroyProgressIndicator('_pIndi_' + AsyncRequest.uniqueId(aTarget[i]) + iRequestId.toString());
			}
		} else {
			if (aTarget == undefined) return;
			AsyncRequest.destroyProgressIndicator('_pIndi' + iRequestId.toString());
		}
};

/**
 * Destroys a progress indicator element
 */
AsyncRequest.destroyProgressIndicator = function(sId) {
	var oChild = $(sId);
	if (oChild)
		oChild.parentNode.removeChild(oChild);
};

/**
 * Creates request object and returns it
 */
AsyncRequest.createRequestObject = function() {
    var xmlHttpObject = null;

    //Mozilla, Opera, Safari, IE since v7
    if (typeof XMLHttpRequest != 'undefined')
        return new XMLHttpRequest();
    else
        try
        {
            //IE since v6
            return new ActiveXObject("Microsoft.XMLHTTP");
        }
        catch(e)
        {
            try
            {
                //IE since v5
                return new ActiveXObject("Msxml2.XMLHTTP");
            }
            catch(e) { }
        }
    return null;
};

AsyncRequest.getElementData = function(oElement) {
	if (!oElement.name) return '';
	switch(oElement.type) {
		case "checkbox":
		 //typically not needed, radio groups will be processed in 'default'
		case "radio":
			if (oElement.checked)
				return oElement.name + '=' + encodeURIComponent(oElement.value) + '&';
			else
				return '';
		break;
		case "select-multiple":
			var sResult = '';
			for (var j = 0; j < oElement.options.length; j++)
				if (oElement.options[j].selected)
					sResult += oElement.name + '=' + encodeURIComponent(oElement.options[j].value) + '&';
			return sResult;
		break;
		//a button has typically no value, so we don't want to send it
		case "submit":
		case "button":
			return '';
		break;
		default: //select-one, text,
			return oElement.name + '=' + encodeURIComponent(oElement.value) + '&';
		break;
	}
	return '';
};

/**
 * Reads data from form and returns it as escaped string
 */
AsyncRequest.getFormData = function(oForm) {
	var sResult = '';
	//from the oForm, get all input data and urlencode it
	for (var i = 0; i < oForm.elements.length; i++)
		sResult += AsyncRequest.getElementData(oForm.elements[i]);
	return sResult;
};

/**
 *  Reads data from form elements in the given container and returns them as escaped string
 */
AsyncRequest.getPartiallyFormData = function(oContainer) {
	var sResult = '';
	var aElements = new Array();
	AsyncRequest.getFormElements(oContainer, aElements);
	//get all input data and urlencode it
	for (var i = 0; i < aElements.length; i++)
		sResult += AsyncRequest.getElementData(aElements[i]);
	return sResult;
}

/**
 * Searches the given container for formular elements and fills the array aElements
 */
AsyncRequest.getFormElements = function(oContainer, aElements) {
	for(var i = 0; i < oContainer.childNodes.length; i++) {
		if (oContainer.childNodes[i].childNodes.length > 0)
			AsyncRequest.getFormElements(oContainer.childNodes[i], aElements);
		//check if the element has a form property (then it should be a form element)
		if (oContainer.childNodes[i].form)
			aElements.push(oContainer.childNodes[i]);
	}
}

AsyncRequest.getNodeData = function(oNode) {
    var sResult = "";
    var aChildNodes = oNode.childNodes();
    for(i = 0; i < aChildNodes.length; i++)
		//check if getAttribute function exists
		if (aChildNodes[i].getAttribute)
			//check if getAttribute("type") has a value
			if (aChildNodes[i].getAttribute("type"))
				//get data
				sResult += AsyncRequest.getElementData(aChildNodes[i]);
	return sResult;
};

/**
 * This function exchanges a table row with a new one
 * When the target is a table row it is only allowed to
 * send the complete code of a single row of a table instead its contents
 * When you ask what the problem is ... the problem is the IE - what else ...
 */
AsyncRequest.updateTableRow = function(oRow, sNewRowHTML) {
	//when the contents are empty, we delete the target row
	if (sNewRowHTML.length === 0) {
		oRow.parentNode.removeChild(oRow);
		return;
	}
	//create temporary div
	var oTempDiv = document.createElement('div');
	//insert a new table including the new row (necessary because of problems with the internet exploder)
	oTempDiv.innerHTML = '<table><tbody>' + sNewRowHTML + '</tbody></table>';

	var oParentNode = oRow.parentNode;
	//ok duplicate ids are forbidden, get the next row
	var oNextRow = oRow.nextSibling;
	//delete old row - remove the possible duplicate index before inserting new rows
	oParentNode.removeChild(oRow);
	//if  next row exists
	if (oNextRow) {
		//add all rows from request
		while(oTempDiv.firstChild.rows.length > 0)
			//the child will be removed from oTempDiv, so we use everytime index 0
			oParentNode.insertBefore(oTempDiv.firstChild.rows[0], oNextRow);
	}
	else
	{
		//there is no next row
		//add all rows from request to the end
		while(oTempDiv.firstChild.rows.length > 0)
			//the child will be removed from oTempDiv, so we use everytime index 0
			oParentNode.appendChild(oTempDiv.firstChild.rows[0]);
	}
};

/**
 * Specialized code to change select contents in IE
 */
AsyncRequest.updateIESelect = function(oSelect, sContent) {
	if(navigator.appName == "Microsoft Internet Explorer") {
		var selectHeader = "<select";
		var selectFooter = "</select>";
		for (var i = 0; i < oSelect.attributes.length; i++)
			if ((oSelect.attributes[i].nodeValue != null) && (oSelect.attributes[i].nodeValue != 0) && (oSelect.attributes[i].nodeValue != ""))
				selectHeader += " " + oSelect.attributes[i].nodeName + "=\"" + oSelect.attributes[i].nodeValue + "\"";
		if (oSelect.style.cssText.length > 0)
			selectHeader += " style=\"" + oSelect.style.cssText + "\"";
		selectHeader += ">";
		oSelect.outerHTML = selectHeader + sContent + selectFooter;
	} else {
		oSelect.innerHTML = sContent;
	}
};

