var xmlHttpReq;
var onAjaxResponse;
var onBeforeAjaxPostback;
var AjaxResponseBuffer = [];
var ResponseType ={
    StartupScript:1,
    ClientScript:2,
    InlineScript:3,
    HiddenField:4,
    Panel:5,
    Array:6,
    Transfer:7,
    JsSourceFile:8,
    ForceRealPostBack:9
};

/* When there is no IpostbackeventHandler control on the page, the __dopostback function is generated at 
 * the end of the rendering. We need to wait for the onload to obtain a reference to the function.   
 */
var rPb = function()
{  
    if (typeof(__doPostBack) == 'function')
        __doAPostBack=__doPostBack;
};
// "Replacement" function used by AjaxDisabler & AjaxPanel with RefreshOnly option
if (typeof(__doPostBack) == 'function')
	rPb();
else if(typeof($) != "undefined")
    $(rPb); 

function FunctionQueueManager(queueCapacity)
{
	var m_Capacity = (queueCapacity)? queueCapacity: Infinity;
	var self = this;
	var m_Requests;
	var m_Count;
	var m_Busy;
	
	function Init()
	{
		m_Requests = [];
		m_Count = 0;
		m_Busy = false;
	}
	
	Init();

	this.Enqueue = function()
	{
		if (m_Count < m_Capacity)
		{
			m_Requests.push(new QueueManagerRequest(arguments));
			m_Count++;
		}
		
		ExecuteNext();
	}
	
	function ExecuteNext()
	{
		if (m_Busy)
			return;
	
		var nextRequest = GetPeek();
		
		if (nextRequest == null)
			return;
			
		m_Busy = true;
		
		nextRequest.Execute();
	}
	
	this.Dequeue = function()
	{
		if (m_Count == 0)
			return;
			
		m_Requests.shift();
		m_Count--;
		
		m_Busy = false;
		
		ExecuteNext();
	}
	
	this.Clear = function()
	{
		Init();
	}

	function GetPeek()
	{
		if (m_Count == 0)
			return null;
			
		return m_Requests[0];
	}
	
	function QueueManagerRequest(args)
	{
		var m_Func = args[0];
		var m_Args = ConvertArgsToArray(args);
		
		this.Execute = function()
		{
			m_Func.apply(this, m_Args);
		}
		
		function ConvertArgsToArray(argsToConvert)
		{
			var result=[];
		
			for(var i=1; i<argsToConvert.length; i++)
				result.push(argsToConvert[i]);
			
			return result;
		}
	}
}

// encapsulate a request and its event target + argument
function BvdXmlHttpRequest(xmlHttpRequest, eventTarget, eventArgument)
{
  this.request=xmlHttpRequest;
  this.requestEventTarget=eventTarget;
  this.requestEventArgument=eventArgument;
}

function CreateXMLHTTP()
{
     var retval=null;
     try
     {
          retval=new ActiveXObject("Msxml2.XMLHTTP");
     }
     catch(e)
     {
          try
          {
               retval=new ActiveXObject("Microsoft.XMLHTTP");
          } 
          catch(oc)
          {
               retval=null;
          }
     }

     if(!retval && typeof XMLHttpRequest != "undefined") 
     {
          retval=new XMLHttpRequest();
          if (retval.overrideMimeType) 
            retval.overrideMimeType('text/xml'); 
     }

     return retval;
}


function ajaxCallBack(xmlHttpReq, callBackFunction)
{
	if( xmlHttpReq && xmlHttpReq.request )
	{
		// See if ajax callback mechanism is finished
		if( xmlHttpReq.request.readyState==4 )
		{
			// Only treat the response if no error occured
			if( xmlHttpReq.request.status==200 )
			{
				parseAjaxResponse(function()
				    {
				        var ret = AjaxResponse();
				        ret.request = xmlHttpReq;
				        ret.content = xmlHttpReq.request.responseText;
				        ret.callBackFunction = callBackFunction;
				        ret.JSTranslations = JSTranslations;
				        return ret;
				     }());
			}
			else
			{
				document.body.style.cursor = "auto";
				m_AjaxPanelPostBackManager.Dequeue();
			}
			return ;
		}
	}
}

function AjaxResponse()
{
    var ret={};
    ret.request = null;
    ret.content = null;
    ret.callBackFunction = null;
    ret.controls = null;
    ret.controlIndex = null;
    ret.foundCtrl = null;
    ret.totalContentSize=0;
    return ret;
}

function parseAjaxResponse(params)
{
    /*
    ResponseText of xmlHttpRequest is formatted as
    $$-$$clientid1$$-$$content1$$-$$
    $$-$$clientid2$$-$$content2$$-$$
    $$-$$clientid3$$-$$content3$$-$$
    (without end-of-line)
    */
    if (params.content != null)
    {
        log.profile("server side");//end
        log.debug("Parsing ajax response");
        params.controls = params.content.split ("$$-$$");
        params.controlsIndex = 0;
        params.foundCtrl = false;
        if (AjaxResponseBuffer === null)
            LoadControls(params);
        else
            AjaxResponseBuffer.push(params);
     }
}

function FlushAjaxResponseBuffer()
{
    if(AjaxResponseBuffer !== null)
    {
        while(AjaxResponseBuffer.length>0)
        {
            LoadControls(AjaxResponseBuffer.shift());
        }
    }
    AjaxResponseBuffer = null;
}

function LoadControls (params)
{
	/*
	Replace each content (linked to the document by its clientid)
	If no content has been replaced, something is not working correctly.
	In that case, do a classic submit
	*/
	var refreshPage = false;
	var n = params.controls.length - 2; // as controlsIndex is increased by 3
	while (params.controlsIndex < n)
	{
		var responseType;
		try{responseType = parseInt(params.controls[params.controlsIndex++], 10);}catch(e){responseType=-1;}
		var id = params.controls[params.controlsIndex++];
		var content = params.controls[params.controlsIndex++];
		switch(responseType)
		{
		    case ResponseType.StartupScript:
		    case ResponseType.ClientScript:
		    case ResponseType.InlineScript:
		    {
			    try
			    {
				    if(window.execScript)
					    window.execScript(content,"javascript");
				    else
					    window.setTimeout(content,0);
			    }
			    catch(e)
			    {
				    log.error("Javascript error during ajax refresh: " + e.name);
			    }
			    break;
			}
		    case ResponseType.HiddenField:
		    {
			    elem = document.forms[0][id];
			    if (elem != null) //The input field already exists
			    {
				    if (elem.type == "hidden") //we only consider the hidden field (for the moment)
				    {
					    elem.value = content;
				    }
			    }
			    else
			    {
				    var inputHiddenField = document.createElement("INPUT");
				    inputHiddenField.type = "hidden";
				    inputHiddenField.id = id; 
				    inputHiddenField.name = id; 
				    inputHiddenField.value = content; 

				    document.forms[0].appendChild(inputHiddenField);
			    }
			    break;
		    }
		    case ResponseType.Panel:
		    {
			    var elem = document.getElementById (id);
			    if (elem != null)
			    {
			        log.debug ("replacing '"+id+"'");
				    params.foundCtrl = true;
				    try{
						elem.innerHTML = content; 
				    }
				    catch(e)
				    {
						if(JsDebug){
							alert("Invalid content for panel: " + id);
						}
				    }
				    params.totalContentSize += content.length;
			    }
			    else
			    {
			        log.warn ("No div was found with id : " + id);
			    }
			    break;
			}
		    case ResponseType.Array:
		    {
			    try
			    {
				    if( window.execScript )
					    window.execScript(id + '=' + content,"javascript");
				    else
					    window.setTimeout(id + '=' + content,0);
			    }
			    catch(e)
			    {}
			    break;
			}
		    case ResponseType.Transfer:
			    document.location = content;
				params.foundCtrl = true;
			    break;
		    case ResponseType.JsSourceFile:
		    {
			    var scriptArray = document.getElementsByTagName("Script");
			    var found = false;
			    for(var i=0; i<scriptArray.length;i++)
			    {
				    if(scriptArray[i].getAttribute("src") == content)
				    {
					    found = true;
					    break;
				    }
			    }
			    if(!found)
			    {
			        log.debug("Dynamically including the '"+content+"' js file");
				    var script = document.createElement("script");
				    script.src = content;
				    script.type = "text/javascript";
				    var cbFunction = function()
					    {
						    LoadControls (params);
					    }
				    script.onload = cbFunction;
				    script.onreadystatechange = function() 
					    {
						    if (this.readyState == 'complete' || this.readyState == 'loaded') 
						    {
							    cbFunction ();
						    }
					    }
				    document.body.appendChild(script);
				    if (!script.complete)
					    return;
			    }
			    break;
			}
			case ResponseType.ForceRealPostBack:
				refreshPage = true;
				params.controlsIndex = n;
				break;
			default:
			    log.error("Unkown Value for ResponseType" + responseType);
			    break;
        }
	}
	if (typeof($) != "undefined")
		JSTranslations = $.extend(params.JSTranslations, JSTranslations);

	log.profile("ajax process"); //end
	if( !params.foundCtrl || refreshPage)
	{
	    if(!params.foundCtrl)
	        log.error ("No content was sent back from the server. Recovery system kicking in.");
		// Restore cursor
		document.body.style.cursor = "auto";

		if (refreshPage)
			__doPostBack("","");
		else if (params.request != null && params.request.requestEventTarget != null) /*Something went wrong and response does not match request. So redo the postback using classic way */
		{
		    if (typeof(__doPostBack) != "undefined")
			    return __doPostBack(params.request.requestEventTarget, params.request.requestEventArgument);
			else
			    document.forms[0].submit ();
		}
	}
	else
	{
	    log.debug("Panel "+params.totalContentSize +" packet "+params.content.length +" ("+Math.round(params.totalContentSize*100/params.content.length)+"%)");
		/*
		Ajax mechanism is now finished.
		Let inform the user of this ajaxPostBack do whatever he
		judges important when every content has been updated
		(like resizing window, ...)
		*/
		if (typeof(onAjaxResponse) != "undefined")
			onAjaxResponse();
			
		if (params.callBackFunction != null)
			params.callBackFunction ();
	}
	// Restore cursor
	document.body.style.cursor = "auto";
	
	m_AjaxPanelPostBackManager.Dequeue();
}

function ajaxPostBack(eventTarget, eventArgument)
{
	ajaxPanelPostBack(true, null, eventTarget, eventArgument);
}

function ajaxPanelPostBack(asynchronousCall, ajaxGroupId, eventTarget, eventArgument) 
{
	
	// If we're using the IFrame ajax request we change the ajaxpanel postback's to normal postback
	// until we find a solution to add setRequestHeader("Content-Transfer-Encoding", "quoted-printable");
	// Without this header input field (including the hidden fields like the viewstate) are incorrectly encoded.
	if (typeof(XMLHttpIFrameRequest) != "undefined")
	{
		// Corrects IE6 not triggering a submit.
		window.setTimeout(function()
		{
			__doPostBack(eventTarget, eventArgument);
		},1);
		return;
	}
	
	if (typeof(onBeforeAjaxPostback) != "undefined")
		onBeforeAjaxPostback();

	m_AjaxPanelPostBackManager.Enqueue(ExecuteAjaxPanelPostBack, asynchronousCall, ajaxGroupId, eventTarget, eventArgument);
}

function ExecuteAjaxPanelPostBack(asynchronousCall, ajaxGroupId, eventTarget, eventArgument)
{	
    log.profile("ajax process");//start
	if (BvdPageDisplayProgressCursor)
		document.body.style.cursor = "wait";
	BvdPageDisplayProgressCursor = true;
	
	if (typeof(document.forms[0].__EVENTTARGET) != "undefined")
	{
	    document.forms[0].__EVENTTARGET.value=eventTarget.split("$").join(":");  
	    document.forms[0].__EVENTARGUMENT.value=eventArgument;
	}

	var url=document.forms[0].action;
	var urlParameters="";
	/*
	Constructs url parameters from the document =
	all inputs
	+
	textareas
	*/
	var inputs=document.getElementsByTagName("input");
	for(var idxInput=0; idxInput<inputs.length; ++idxInput)
	{
		var name=inputs[idxInput].name;
		var type = inputs[idxInput].type;
		if ((type == "hidden") ||(type == "text") ||(type == "password") || (((type == "checkbox") || (type == "radio")) && (inputs[idxInput].checked)))
		{
			var value = encodeURIComponent(inputs[idxInput].value);
			urlParameters=urlParameters+name+"="+value;
			urlParameters=urlParameters+"&";
		}
	}
		
	var textareas=document.getElementsByTagName("textarea");
	for(var idxTextArea=0; idxTextArea<textareas.length; ++idxTextArea)
	{
		urlParameters=urlParameters+textareas[idxTextArea].name+"="+encodeURIComponent(textareas[idxTextArea].value);
		urlParameters=urlParameters+"&";
	}

	var selects=document.getElementsByTagName("select");
	for(var idxSelect=0; idxSelect<selects.length; ++idxSelect)
	{
		urlParameters=urlParameters+selects[idxSelect].name+"="+encodeURIComponent(selects[idxSelect].value);
		urlParameters=urlParameters+"&";
	}
		
	/*
	Indicates it is an ajaxPostBack for the server to format the response
	accordingly
	*/
	urlParameters=urlParameters+"ajaxids="+eventTarget;
	if (ajaxGroupId != null && ajaxGroupId.length > 0)
			urlParameters=urlParameters+"&ajaxgroupid="+ajaxGroupId;
		
	var xmlHttpReq=new BvdXmlHttpRequest(CreateXMLHTTP(), eventTarget, eventArgument);
	
	xmlHttpReq.request.open('POST', url, asynchronousCall);
	
	if 	(asynchronousCall)
		xmlHttpReq.request.onreadystatechange = function () { ajaxCallBack(xmlHttpReq, null);};
	
	xmlHttpReq.request.setRequestHeader("Content-Transfer-Encoding", "quoted-printable");
	xmlHttpReq.request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
	xmlHttpReq.request.setRequestHeader("Content-length", urlParameters.length);		
	log.profile("server side");//start
	log.debug("refresing group : "+ajaxGroupId)
	xmlHttpReq.request.send(urlParameters);
	if (typeof(document.forms[0].__EVENTTARGET) != "undefined")
	{
			document.forms[0].__EVENTTARGET.value = "";  
			document.forms[0].__EVENTARGUMENT.value = "";
	}	
	if (!asynchronousCall)
		ajaxCallBack(xmlHttpReq, null);
}

// UrlParamaters 
// Call Back function is used to be notified of the completion of this ajax request
// The doGetRequest is used when we want to use the browser caching mechanism
function ajaxRequest(url, urlParameters, callBackFunction, doGetRequest, skipCache) 
{
	if (typeof(doGetRequest) == "undefined") doGetRequest = false;
	if (typeof(skipCache) == "undefined") skipCache = false;
	/* 
	Indicates this is an ajaxRequest for the server to format the response
	accordingly
	*/
	if(doGetRequest)
	{
		url +="&AjaxRequest=1";
		if (skipCache)
		{
			url += "&ts=" + new Date().getTime();
		}
	}
	else
		urlParameters += "&AjaxRequest=1";
	var xmlHttpReq=new BvdXmlHttpRequest(CreateXMLHTTP());
	
	if (BvdPageDisplayProgressCursor)
		document.body.style.cursor = "wait";
	BvdPageDisplayProgressCursor = true;
		
	xmlHttpReq.request.open(doGetRequest?"GET":"POST", url, true);
  
	xmlHttpReq.request.onreadystatechange = function () { ajaxCallBack(xmlHttpReq, callBackFunction);};
	xmlHttpReq.request.setRequestHeader("Content-Transfer-Encoding", "quoted-printable");
	xmlHttpReq.request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
	if (!doGetRequest)
		xmlHttpReq.request.setRequestHeader("Content-length", urlParameters.length); 
	xmlHttpReq.request.send(urlParameters);
}

m_AjaxPanelPostBackManager = new FunctionQueueManager(5);
