function AJAX() {
	this.requestor = null;
    this.handlerFunc = null;
    
    this.getNewRequestor = function() {
		var requestor;
		if (window.XMLHttpRequest) {
			// branch for native XMLHttpRequest object
			requestor = new XMLHttpRequest();
		} else if (window.ActiveXObject) {
			// branch for IE/Windows ActiveX version
			requestor = new ActiveXObject("Microsoft.XMLHTTP");
		}
		return requestor;
    }
    
    this.reset = function() {
    	this.requestor = this.getNewRequestor();
    	if (this.handlerFunc) {
	    	this.requestor.onreadystatechange = this.handlerFunc;
	    }
    }
    
    this.sendRequest = function(url,method,content,userName,password,asyncFlag) {
		this.reset();
    	method = method||"GET";
    	asyncFlag = (asyncFlag == false)?false:true;
    	try {
    	    if (typeof(DEBUG) != "undefined" && DEBUG) debug("Requesting " + url);
    	} catch (e) {}
    	this.requestor.open(method,url,asyncFlag);//,userName,password);
    	if (method.toLowerCase() == "post") {
	    	this.requestor.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
	    }
    	this.requestor.send(content);
    	
    	return this.requestor;
    }
    
    this.setHandler = function(func) {
        this.handlerFunc = func;
    	this.requestor.onreadystatechange = func;
    }
    
    this.reset();
}
AJAX.READY_STATE_UNINITIALIZED = 0;
AJAX.READY_STATE_LOADING = 1;
AJAX.READY_STATE_LOADED = 2;
AJAX.READY_STATE_INTERACTIVE = 3;
AJAX.READY_STATE_COMPLETE = 4;
/**
* call like so:
*	var handler = function() {someMethodToDoSomething()};
*	var caller = new AJAXMethodCaller("com.catylist.ajax.MappedPropertiesProvider","populateFromIDs",handler);
*	caller.addParameter(AJAXMethodCaller.INTEGER_ARRAY_TYPE,ids);
*	caller.call();
*
*/
function AJAXMethodCaller(className, methodName, handler, isPostRequest) {
	this.className = className;
	this.methodName = methodName;
	this.isPostRequest = isPostRequest||false;
	this.parameters = new Array();
	this.requestParameters = new Array();
	this.ajax = new AJAX()
    if (handler) {
        this.ajax.setHandler(handler);
    }
	this.callCompleted = true;
    
    this.setSuccessfulResponseXMLHandler = function(responseHandler) {
        var thisAjax = this.ajax;
        this.ajax.setHandler(function() {
            var requestor = thisAjax.requestor;
            if (requestor.readyState == AJAX.READY_STATE_COMPLETE && (!requestor.status || requestor.status == 200)) {
                responseHandler(requestor.responseXML);
            }
        });
    }
	
	this.addParameter = function(type) {
		var values = new Array();
		//arguments.slice() doesn't work
		for (var i=1;i<arguments.length;i++) {
			values.push(arguments[i]);
		}
		if (isArray(values[0])) {
			values = values[0];
		}		
		this.parameters.push(new Parameter(type,values));
	}

	this.addRequestParameter = function(key,value) {
		var values = null;
		if (isArray(value)) {
			values = value
		} else {
			values = new Array();
			values.push(value);
		}
		this.requestParameters[key] = values;
	}
	
	this.clearParameters = function() {
		this.parameters = new Array();
		this.requestParameters = new Array();
	}
	
	this.completeCall = function() {
		this.callCompleted = true;
	}
	
	this.call = function(noCache) {
		this.callCompleted = false;
		var requestParams = "call=" + this.className + "." + this.methodName + "(";
		requestParams += mapFunction(function(x){return x.serialize()},this.parameters).join(",");
		requestParams += ")";
		for (var key in this.requestParameters) {
			for (var i=0;i<this.requestParameters[key].length;i++) {
				requestParams = requestParams + "&" + key + "=" + escape(this.requestParameters[key][i]);
			}
		}

		if (AJAXMethodCaller.ALWAYS_NOCACHE || noCache) {
			requestParams += "&noCache=" + new Date().getTime();
		}
		
		var url = "/jsp/common/ajax_provider.jsp";
		
		var request;
		if (this.isPostRequest) {
			request = this.ajax.sendRequest(url, "POST", requestParams);
		} else {
			request = this.ajax.sendRequest(url + "?" + requestParams);
		}
		return request;
	}
	

}
AJAXMethodCaller.INTEGER_ARRAY_TYPE = "[Ljava.lang.Integer;";
AJAXMethodCaller.STRING_ARRAY_TYPE = "[Ljava.lang.String;";
AJAXMethodCaller.DOUBLE_ARRAY_TYPE = "[Ljava.lang.Double;";

AJAXMethodCaller.ALWAYS_NOCACHE = false;

function Parameter(type,paramValues) {
	this.value = paramValues;
	this.type = type;
    
    this.serialize = function() {
		var paramValues = this.value;
		var value = "";
		if ((paramValues.length > 1 && this.type.substring(0,2) == "[L")) {
			var newValues = new Array();
	        for (var i = 0; i < paramValues.length; i++) {
	            newValues[i] = new String(escapeForCall(paramValues[i]));
	        }
			value = newValues.join("|");
		} else if (!isArray(paramValues)) {
			value = escapeForCall(paramValues);
		} else if (paramValues.length && paramValues[0] != null) {
			value = escapeForCall(paramValues[0]);
		}

        return this.type + " " + escape(value);
    }
}
function escapeForCall(value) {
	value=new String(value);
    return value.toString().replace(/\//g, "//").replace(/,/g, "/,").replace(/\|/g, "/|");
}

function isArray(guy) {
	return (guy instanceof Array || typeof guy == "array");
}

function mapFunction(func, array) {
	var results = new Array();
	for (var i=0;i<array.length;i++) {
		results.push(func(array[i]));
	}
	return results;
}

function filterFunction(func, array) {
    var results = new Array();
    for (var i=0;i<array.length;i++) {
        if (func(array[i])) {
            results.push(array[i]);
        }
    }
    return results;
}

/* AJAXForm can be used to do an inline get/post of any HTML form.
 *
 * Use like so:
 * var myForm = new AJAXForm('formName');
 * myForm.handleSuccess = new function(requestor) {
 *     // maybe do something with requestor.responseText
 * }
 * ...
 * myForm.submit();
 */
function AJAXForm(formName) {
    this.formName = formName;
    
    this.submit = function() {
    	var ajax = new AJAX();
    	
    	var self = this;
    	function handler() {
			var req = ajax.requestor;
	        if (req.readyState == AJAX.READY_STATE_COMPLETE) {
                self.getForm().style.cursor = 'auto';
	    
            	if (!req.status || req.status == 200) {
            	   self.handleSuccess(req);
               	} else {
           		   self.handleFailure(req);
               	}
	        }
    	}
    
		ajax.setHandler(handler);
		
        try {
	    	this.getForm().style.cursor = 'wait';
    
            ajax.sendRequest(this.getAction(), this.getMethod(), this.getParams());
            
        } catch (e) {
	    	this.getForm().style.cursor = 'auto';
            this.handleFailure(e);
        }
    }
    
    this.handleFailure = function(requestor) {
        window.status = 'form submit failed';
    }
    
    this.handleSuccess = function(requestor) {
        alert(requestor.responseText);
    }

    this.getForm = function() {
        var form = document.forms[this.formName];
        if (!form) {
            // IE 7 won't pick up forms via [] w/ non-alphanumeric characters in the name.
            for (var i = 0; i < document.forms.length; i++) {
                if (document.forms[i].name == this.formName) {
                    form = document.forms[i];
                    break;
                }
            }
        }
        return form;
	}

    this.getAction = function() {
        var actionAttr = this.getForm().attributes.getNamedItem('action');
        if (actionAttr) {
            return actionAttr.value;
        } else {
            return "";
        }
    }
    
    this.getMethod = function() {
        return this.getForm().method;
    }
    
    this.getParams = function() {
        var params = '';
        
        var elements = this.getForm().elements;
        for (var i = 0; i < elements.length; i++) {
        	if (elements[i].name) {
	            if (elements[i].tagName == 'INPUT' || elements[i].tagName == 'TEXTAREA') {
	                if (elements[i].type == 'checkbox' || elements[i].type == 'radio') {
	                    if (elements[i].checked) {
	                        params += elements[i].name + '=' + encodeURIComponent(elements[i].value) + '&';
	                    }
	                } else {
	                    params += elements[i].name + '=' + encodeURIComponent(elements[i].value) + '&';
	                }
	            
	            } else if (elements[i].tagName == 'SELECT') {
	                var options = elements[i].options;
	                for (var j = 0; j < options.length; j++) {
	                    if (options[j].selected) {
	                        params += elements[i].name + '=' + encodeURIComponent(options[j].value) + '&';
	                    }
	                }
	            }
        	}
        }
        
        return params;
    }
}

function AHAH() {
	if (! AHAH.__instance) {
		AHAH.__instance = this;
	} else {
		return AHAH.__instance;
	}	
	
	/**
	* makes a request to pPath and injects the results into pDomElement
	* pDomElement: an actual dom element, not an id or jquery object
	* pPath: url relative to the root of the site.  Can include a query string.
	* pEvalScript: evaluate <script> tags found in the result
	* pCallback: function called when the data has been loaded 
	* pReplace: replace the given element with results of the AHAH call (rather than replace content)
	**/
	this.load = function(pDomElement, pPath, pCallback, pEvalScript, pReplace) {
		var handler = function() {
            var req = caller.ajax.requestor;
            if (req.readyState == AJAX.READY_STATE_COMPLETE) {
                if (!req.status || req.status == 200) {
                    var html = req.responseText;
                    
                    if (pReplace) {
                        $(pDomElement).replaceWith(html);
                    } else {
                        pDomElement.innerHTML = html;
                    }
                    
                    if (pEvalScript) {
                        _evalScriptNodes(pDomElement);
                    }
                } else {
                    window.status = "An error has occurred: " + req.statusText;
                }
                if (pCallback) {
	                pCallback(req);
	            }
            }
		}
		var caller = new AJAXMethodCaller("com.catylist.ajax.AJAXProvider", "ahah", handler,true);
		caller.addParameter("java.lang.String",pPath);
		caller.call(true);
	}
	
	function _evalScriptNodes(rootNode) {
        // IE is a little twitchy w/ script tags inserted via innerHTML:
        // There _must_ be some content before a script tag or it will be tossed out.
        var scriptNodes = rootNode.getElementsByTagName('SCRIPT');
        for (var i = 0; i < scriptNodes.length; i++) {
            var src = scriptNodes[i].getAttribute('src');
            if (src) {
                /* let's not do anything here yet
                var scriptNode = document.createElement('script');
                
                var type = scriptNodes[i].getAttribute('type')||'text/javascript';
                scriptNode.setAttribute('type', type);
                scriptNode.setAttribute('src', src);
                
                document.documentElement.appendChild(scriptNode);
                
                this.toDestroy.push(scriptNode);
                */
            } else {
                eval(scriptNodes[i].text);
            }
        }
	}
}