function JStype(obj){
	var type = typeof obj;
	if (type == 'object' && obj.nodeName){
		switch(obj.nodeType){
			case 1: return 'element';
			case 3: return 'textnode';
		}
	}
	if (type == 'object' || type == 'function'){
//		if (typeof(obj.constructor) != 'undefined') {
//			switch(obj.constructor){
//				case Array: return 'array';
//				case RegExp: return 'regexp';
//				case Class: return 'class';
//			}
//		}

		if (typeof obj.length == 'number'){
			if (obj.item) return 'collection';
			if (typeof(obj.callee) != 'undefined') {		
				if (obj.callee) return 'arguments';
			}
		}
	}
	return type;
};


function urlencode(text) {
     text = encodeURIComponent(text);
     text = text.replace(/\//g,"%2F");
     text = text.replace(/\?/g,"%3F");
     text = text.replace(/=/g,"%3D");
     text = text.replace(/&/g,"%26");
     text = text.replace(/@/g,"%40");
     text = text.replace(/\+/g,"%2B");
     return text;   
}


var requests = new Array();
var requestMap = new Array();
var ajaxDefaultTimeout = 1000000;

function callTimeout(id) {
    var req = requestMap[id];
    if ((req) && (req.isBusy())) {
        req.internalTimeout();
    }
    requestMap[id]=false;
}

function XMLRPC () {	

	var status = null;
	var url = null;	
	var req = null;
	var msgCount = 0;	
	var inProgress = false;
	var isComplete = false;
    var timeoutRef = false;
    var reqID = false;
    this.asynchronous = true;
		
	var oThis = this;
	
	// checks to see if we have too many messages in log
	var internalCanMsg = function(){
		msgCount++;
		return msgCount < 100;
	}
	
	// adds message to internal log
	var internalOnLog = function(msg){
		if(oThis.onLog && internalCanMsg()) {
			oThis.onLog(msg);
		}
	}
	
	// adds message to internal error handler
	var internalOnError = function(msg){
		if(oThis.onError && internalCanMsg()) {
			oThis.onError(msg);
		}
	}	
	
	// tells us whether we are busy waiting for the response to another requst
	var internalIsBusy = function(){
		return inProgress && !isComplete;
	}
    
	
	// internal callback function for the browser; it is called when a state of a request object changes
	var internalRequestComplete = function() {
				
		var STATE_COMPLETED = 4;
		var STATUS_200 = 200;

		clearTimeout(this.timeoutRef);		
		if (!internalIsBusy()) {
			internalOnError("internalRequestComplete: error - no request submitted");
		}
		internalOnLog("internalRequestComplete: readyState " + req.readyState);
		
		if (req.readyState == STATE_COMPLETED) {
			status = req.status;
			inProgress = false;
			isComplete = true;
            ajax_decrement(); 
			internalOnLog("internalRequestComplete: status " + status);
			
			if (status == STATUS_200) {
				internalOnLog("internalRequestComplete: calling callback on content with length " + req.responseText.length + " chars");			
                if(oThis.onComplete) {
                    
					oThis.onComplete(req.responseText, req.responseXML);
				}				 
				internalOnLog("internalRequestComplete: complete on " + new Date());
			} else {
				internalOnError("internalRequestComplete: error - bad status while fetching " + url);
			}
		} else {
			// we need to review other state codes for XMLRPC provider
		}
	}	
	
	// call this function to figure out version of this class
	this.getVersion = function(){
		return "1.0.0";
	}
	
	// call this function to figure out if current browser supports XML HTTP Requests
	this.isSupported = function(){
		var nonEI = window.XMLHttpRequest;
		var onIE = window.ActiveXObject;
		
		if (onIE) {	    		
			onIE = new ActiveXObject("Microsoft.XMLHTTP") != null;
		}
		
		return window.XMLHttpRequest || onIE;
	}
	
	// call this function to find out if more calls are possible and if request has been completely received 
	this.isBusy = function(){
		return internalIsBusy();
	}

    this.internalTimeout = function() {
        this.abort();
        this.onError("Request Timeout");
    }
    
    this.onStartRequestTimeout = function(url, timeout) {
        if (timeout<=0) return;
        var ts = new Date();
        var id = ts.toUTCString() + '-' + url;
        this.reqID = id;
        requestMap[id]=this;
        this.timeoutRef = setTimeout("callTimeout('"+id+"');", timeout);  
    }		

	//  call this function to submit new request
	this.get = function(_url, timeout){	
		if (internalIsBusy()) {
			internalOnError("submit: error - busy processing another request " + _url);			
		}	
		
		msgCount = 0;
		internalOnLog("submit: started on " + new Date() + " for " + _url);
		ajax_status("GET: "+_url);
        ajax_increment();		
		url = _url;	
		status = null;
		inProgress = true;
		isComplete = false;
        if (!timeout) {
            timeout = ajaxDefaultTimeout;
        }
		this.onStartRequestTimeout(_url, timeout);
	    if (window.XMLHttpRequest) {
	    
	    	// branch for native XMLHttpRequest object
			internalOnLog("submit: using XMLHttpRequest()");
	    
	        req = new XMLHttpRequest();
			if(this.asynchronous) {
				req.onreadystatechange = internalRequestComplete;
			}
	        req.open("GET", url, true);
	                
        	req.send(null);	

			if(!this.asynchronous) {
				return req.responseText;
			}
	        	    
	    } else { 
	    	    	
	    	// branch for IE/Windows ActiveX version
	    	if (window.ActiveXObject) {	    		
		        req = new ActiveXObject("Microsoft.XMLHTTP");
		        if (req) {
		        
					internalOnLog("get: using Microsoft.XMLHTTP");
		        
					if(this.asynchronous) {
						req.onreadystatechange = internalRequestComplete;
					}

		            req.open("GET", url, true);
			    	req.setrequestheader("Pragma","no-cache");
		   	    	req.setrequestheader("Cache-control","no-cache");
		           
		        	req.send();
		        		
					if(!this.asynchronous) {
						return req.responseText;
					}
		        } else {
					internalOnError("submit: error - unable to create Microsoft.XMLHTTP");
		        }
		    } else {
				internalOnError("submit: error - browser does not support XML HTTP Request");
		    }
	    }
		
		internalOnLog("submit: complete");
	}
	
	//  call this function to submit new request
	this.post = function(_url, param_obj, timeout){	
		if (internalIsBusy()) {
			internalOnError("submit: error - busy processing another request " + _url);			
		}	
		ajax_status("POST: "+_url);
        ajax_increment();
		msgCount = 0;
		internalOnLog("submit: started on " + new Date() + " for " + _url);
				
		url = _url;	
		status = null;
		inProgress = true;
		isComplete = false;
        var parameters = '';
        if (!timeout) {
            timeout = ajaxDefaultTimeout;
        }
        
		this.onStartRequestTimeout(_url, timeout);
        for (var idx in param_obj) {
        	paramValue = param_obj[idx];
			if (typeof(JStype) != 'undefined') {
				paramValueType = JStype(paramValue);				
			} else {
				paramValueType = typeof(paramValue);
			}
					
        	if(paramValueType == 'object' || paramValueType == 'array') {
        		for(valueElemIdx in paramValue) {

					if (typeof(JStype) != 'undefined') {
						valueElemIdxType = JStype(paramValue[valueElemIdx]);				
					} else {
						valueElemIdxType = typeof(paramValue[valueElemIdx]);
					}

					if (valueElemIdxType != 'function') {
	        			if(valueElemIdxType == 'string') {
		        			parameters = parameters + idx + "[" + valueElemIdx + "]=" + urlencode(paramValue[valueElemIdx]) + "&";
        				} else {
	        				parameters = parameters + idx + "[]=" + urlencode(paramValue[valueElemIdx]) + "&";
        				}
					}
        		}
        	} else {
	            parameters = parameters+idx+"="+urlencode(paramValue) + "&";
        	}
        }
	    if (window.XMLHttpRequest) {
	    	// branch for native XMLHttpRequest object
	        req = new XMLHttpRequest();
	        if (req) {
				internalOnLog("post: using XMLHttpRequest()");
			}        	    
	    } else {   	
	    	// branch for IE/Windows ActiveX version
	    	if (window.ActiveXObject) {	    		
		        req = new ActiveXObject("Microsoft.XMLHTTP");
		        if (req) {
					internalOnLog("post: using Microsoft.XMLHTTP");
		        }
		    } 
	    }
		if (req) {
			if(this.asynchronous) {
				req.onreadystatechange = internalRequestComplete;
			}

	        req.open('POST', url, this.asynchronous);
			req.setRequestHeader("Pragma","no-cache");
		   	req.setRequestHeader("Cache-control","no-cache");
			req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
			req.setRequestHeader("Content-length", parameters.length);
			req.setRequestHeader("Connection", "close");
			req.send(parameters);
			
			if(!this.asynchronous) {
				return req.responseText;
			}
		} else {
			internalOnError("submit: error - browser does not support XML HTTP Request");
		}
		internalOnLog("submit: complete");
	}
	
	// call this function to abort current request
	this.abort = function(){
		internalOnLog("abort: " + url);
		
		if (!internalIsBusy()) {
			internalOnError("abort: error - no request submitted");			
		}
	
		onComplete = null;		
		req.abort();
	}	

	// call this function to find out current url
	this.getUrl = function(){
		return url;
	}
	
	// call this function to find out HTTP status code after response completes
	this.getStatus = function(){
		return status;
	}	
	
	// please can override this; this is function called when fatal error occurs
	this.onError = function(msg){
            window.status=msg;
	} 
	
	// user can override this; this function  is called when log message is created	
	this.onLog = function(msg) {
           // window.status=msg;
	}	
	
	// user can override this;  this function is called when response is received without errors
	this.onComplete = function(responseText, responseXML){
	}
	
}

function get_form_data(element_id) {
    var result = '';
    var elem = document.getElementById(element_id);
    var param_obj = get_form_params(elem);
    for (var idx in param_obj) {
         result = result+idx+"="+urlencode(param_obj[idx]) + "&";
    }
    return result;
}

function get_form_params_by_id(element_id) {
    var result = '';
    var elem = document.getElementById(element_id);
    var param_obj = get_form_params(elem);
    return param_obj;
}

function get_form_params(form) {
        var arrayCounters = new Object();
        var params = new Object();    
			 //construct values
			 childList = form.getElementsByTagName('*');
				for( var e = 0; e < childList.length; e++ ) {
				 thisInput = childList[e];
					//only get form elements
					if( thisInput.nodeName.toLowerCase() == 'input' ) {
					 thisElmType = thisInput.getAttribute('type');
					 thisElmType = ( thisElmType == null ) ? 'text' : thisElmType.toLowerCase();
					}
					else if( thisInput.nodeName.toLowerCase() == 'button' ) {
					 thisElmType = 'button';
                     
					}
					else if( thisInput.nodeName.toLowerCase() == 'textarea' ) {
					 thisElmType = 'textarea';
					}
					else if( thisInput.nodeName.toLowerCase() == 'select' ) {
					 thisElmType = 'select';
					}
					else {
					 continue;
					}
					//do not handle elements with no names
					if( thisInput.name == '' || thisInput.name == 'undefined' ) {
					 continue;
					}
					/*
						Account for different element types
					*/
                    //file upload
					if( thisElmType == "file" ) {
					 userFuncVal = userFunc( null , AJForm.STATUS['FILE_UPLOAD_FAILED'] , "Unable to handle file uploads." );
					 return userFuncVal;
					}
					//checkbox | radio
					else if( thisElmType == "checkbox" || thisElmType == "radio" ) {
						if( !thisInput.checked ) {
						    continue;
						} 
					}
				
					//JMM- 11/18 fix 3 - added this clause for multiple select handling
					if (thisElmType == 'select') {
					 selectName = thisInput.name;
					 selectValue = thisInput.value
                    
                     if (!thisInput.multiple) {
					    for (var sIndex=0; sIndex<thisInput.length; sIndex++) {
							if (thisInput.options[sIndex].selected) {
							 controlName = thisInput.name;
							 controlValue = thisInput.options[sIndex].value;	 
							}
						}
                        params[controlName]=controlValue;
                        
					 } else {
                     
                        for (var sIndex=0; sIndex<thisInput.length; sIndex++) {
							if (thisInput.options[sIndex].selected) {
							 controlName = thisInput.name + "["+sIndex+"]";
							 controlValue = thisInput.options[sIndex].value;
							 params[controlName]=controlValue;
							}
						}
                        
                     }
                    } else {
						//JMM 11/18 fix 1 = switched location of this and dataStr code below
						//argument separator
					     //encode the data
					     controlName = thisInput.name;
					     controlValue = thisInput.value;
                         var reg = /[a-z0-9_]\[\]/im;
                         if (reg.test(controlName)) {
                            controlBaseName = controlName.slice(0, controlName.length-2);
                            var ac = 0;
                            if (arrayCounters[controlBaseName]) {
                                ac = arrayCounters[controlBaseName]; 
                            }
                            arrayCounters[controlBaseName] = ac+1;
                            controlName = controlBaseName + "["+ac+"]";
                            
                         }
                         params[controlName]=controlValue;
					}
                    
                    
				}
                return params;
}

function array_contains(ar, search) {
    for (i in ar) {
        if (ar[i]==search) {
            return true;
        }
    }
    return false;
}

function add_prefixed_attributes(src, prefix, target) {
    for (var name in src) {
        target[prefix + name] = src[name];
    }
    return target;
}

function get_prefixed_attributes(src, prefix) {
    var target = new Object();
    var plen = prefix.length;
    for (var name in src) {
        if (name.length<=plen) continue;
        var p = name.substring(0,plen);
        if (p!=prefix) continue;
        var sname = name.substr(plen);
        target[sname] = src[name];
    }
    return target;
}


function restore_form_params(form, params) {   
			 //construct values
			 childList = form.getElementsByTagName('*');
			 for( var e = 0; e < childList.length; e++ ) {
				 thisInput = childList[e];
					//only get form elements
					if( thisInput.nodeName.toLowerCase() == 'input' ) {
					 thisElmType = thisInput.getAttribute('type');
					 thisElmType = ( thisElmType == null ) ? 'text' : thisElmType.toLowerCase();
					}
					else if( thisInput.nodeName.toLowerCase() == 'button' ) {
					 thisElmType = 'button';
                     
					}
					else if( thisInput.nodeName.toLowerCase() == 'textarea' ) {
					 thisElmType = 'textarea';
					}
					else if( thisInput.nodeName.toLowerCase() == 'select' ) {
					 thisElmType = 'select';
					}
					else {
					 continue;
					}
					//do not handle elements with no names
					if( thisInput.name == '' || thisInput.name == 'undefined' || thisInput.name == undefined) {
					 continue;
					}
                    var controlName = thisInput.name;
                    var controlIsArray = false;
                    var reg = /[a-z0-9_]\[\]/im;
                    if (reg.test(controlName)) {
                        controlBaseName = controlName.slice(0, controlName.length-2);
                        controlIsArray = true;
                    }
                    if (controlName) {
                        var controlValue = params[controlName];
                    }
                    
					/*
						Account for different element types
					*/
					//file upload
					if( thisElmType == "file" ) {
					 userFuncVal = userFunc( null , AJForm.STATUS['FILE_UPLOAD_FAILED'] , "Unable to handle file uploads." );
					 return userFuncVal;
					} else if( thisElmType == "checkbox" || thisElmType == "radio" ) {
                            if (params[controlName]==thisInput.value) {
                             thisInput.checked = true;
						    } else {
                             thisInput.checked = false;
                            }
                            if (controlIsArray) {
                                if (array_contains(params[controlBaseName], thisInput.value)) {
                                    thisInput.checked = true;
                                } else {
                                    thisInput.checked = false;
                                }
                            }
                            continue;
                    } 
                    
                    if (thisElmType == 'select') {
                        
                        if (!thisInput.multiple) {
                                
                                for (var sIndex=0; sIndex<thisInput.length; sIndex++) {
					 		        if (controlValue) {
                                        if (thisInput.options[sIndex].value == controlValue) {
                                                thisInput.options[sIndex].selected = true;
                                        } else {
                                            thisInput.options[sIndex].selected = false; 
                                        }
                                    } else {
                                            thisInput.options[sIndex].selected = false;
                                    }
                                }              
					        
                        } else {
                                for (var sIndex=0; sIndex<thisInput.options.length; sIndex++) {
                                        thisInput.options[sIndex].selected = false;    
                                        if (controlValue && controlValue.length) {
                                            for (var kx=0;kx<controlValue.length;kx++) {
					 		                    if (controlValue[kx] == thisInput.options[sIndex].value) {
					  		 	                      thisInput.options[sIndex].selected = true;
                                                      break;
					  		                    } 
                                            }
                                        }
                                        
					 	        }                        
                        }
                    } else {
						    //JMM 11/18 fix 1 = switched location of this and dataStr code below
						    //argument separator
					     //encode the data
                           if (controlValue!=undefined) {
					            thisInput.value=controlValue;
                           }
					}
                       
				}
                return true;
}


function ajax_getDocElement(name) {
  if (document.all) {
  	return document.all[name];
  } else if (document.getElementById) {
  	return document.getElementById(name);
  } 
  return false;
}

function ajax_targeted_response(responseText, responseXML) {
    var trg = ajax_getDocElement(this.target);
	var content = '';
    if (responseText.substr(0,1) == '{') {
    	// the response is a JSON object.
	    eval('var response='+responseText+';');
	    if (typeof(response.view) != 'undefined') {
	    	content = response.view;
	    }
    } else {
		// the response is a plain text or html response
    	content = responseText;	
    }
    // replace the specified attribute of the target element with the received content
    trg[this.targetAttribute]=content;    
}

function ajax_json(responseText, responseXML) {
    eval('var targets='+responseText+';');
    for(var k in targets) {
        var elem = getDocElement(k);
        var content = targets[k];
        if (k=='execute') {
            continue;
        }
        if (elem) {
            if (elem.tagName=='div') {
                elem.innerHTML = content;
            } else if (elem.tagName=='input') {
                elem.value = content;
            } else if (elem.tagName=='span') {
                elem.innerHTML = content;
            } else if (elem.tagName=='textarea') {
                elem.value = content;
            } else {
                elem.innerHTML = content;
            }
        }
    }
    if (targets['execute']) {
        eval(targets['execute']);
    }
}

var ajax_count=0;

function ajax_status(stat) {
    var el = document.getElementById('ajax_status');
    if (el) {
        el.innerHTML = stat;
    }
}

function set_ajax_count(ct) {
    var el = document.getElementById('ajax_count');
    ajax_count = ct;
    if (el) {
        el.innerHTML = ct;
    }
}

function ajax_increment() {
    ajax_count++;
    set_ajax_count(ajax_count);
}

function ajax_decrement() {
    ajax_count--;
    if (ajax_count<0) {
        ajax_count = 0;
    }
    set_ajax_count(ajax_count);
}


// Beendet alle laufenden Requests.
function ajax_clear() {
    for (var i=0;i<requests.length;i++) {
        requests[i].abort();
    }
    requests = new Array();
}

function ajax_submit_form(form, callback, validator) {
                
                var request = new XMLRPC();
                requests[requests.length]=request;
				request.onComplete = callback;
				var params = get_form_params(form);
                if (validator) {
                    validator(params);
                }
                request.params = params;
				request.post(form.action, params);
}

function ajax_submit(url, callback, params) {
    var request = new XMLRPC();
    requests[requests.length]=request;
    request.params = params;
	request.onComplete = callback;
    if (params) {
        request.post(url, params);
    } else {
	    request.get(url);
    }
}

function ajax_submit_synchronous(url, params) {
    var request = new XMLRPC();
    request.asynchronous = false;
    requests[requests.length]=request;
    request.params = params;
    if (params) {
        request.post(url, params);
    } else {
	    request.get(url);
    }
    return request.respons
}

function ajax_submit_to(url, target, targetAttribute, params) {
                var request = new XMLRPC();
                requests[requests.length]=request;
                request.params = params;
                request.target = target;
                request.targetAttribute = targetAttribute;
				request.onComplete = ajax_targeted_response;
				request.get(url);
}

function ajax_submit_to(url, target, targetAttribute) {
                var request = new XMLRPC();
                requests[requests.length]=request;
                request.target = target;
                request.targetAttribute = targetAttribute;
				request.onComplete = ajax_targeted_response;
				request.get(url);
}

function ajax_submit_to_callback(url, target, targetAttribute, callback) {
                var request = new XMLRPC();
                requests[requests.length]=request;
				request.target = target;
                request.targetAttribute = targetAttribute;
//				request.onComplete = ajax_targeted_response;
				request.onComplete = callback;
				request.get(url);
}



				
				
				
function ajax_submit_form_to(form, target, targetAttribute, validator) {
                var request = new XMLRPC();
                requests[requests.length]=request;
                request.target = target;
                request.targetAttribute = targetAttribute;
				request.onComplete = ajax_targeted_response;
				var params = get_form_params(form);
                if (validator) {
                    validator(params);
                }
                request.params = params;
				request.post(form.action, params);
}

function ajax_submit_form_to_url(url, form, target, targetAttribute, validator) {
                var request = new XMLRPC();
                requests[requests.length]=request;
                request.target = target;
                request.targetAttribute = targetAttribute;
				request.onComplete = ajax_targeted_response;
				var params = get_form_params(form);
                if (validator) {
                    validator(params);
                }
                request.params = params;
                
				request.post(url, params);
}
