function $(id)
{
	return document.getElementById(id);
}

//Darn you IE: http://msdn2.microsoft.com/en-us/library/ms536438.aspx
document.expando = true;
if (window.ActiveXObject) {
 window.innerWidth=window.screen.availWidth;
 document.getElementsByName = function(name) {
  var found = [];
  for(i = 0; i < document.all.length; i++){
   if(document.all(i).attributes && document.all(i).attributes.getNamedItem("name") && document.all(i).attributes.getNamedItem("name").value==name) {
   	found.push(document.all(i));
   }
  }
  return found;
 }
}

// a function/instance wrapper that supports Function.call and Funciton.apply -like methods
function Callback(fn, instance, appendedArgs)
{
	this.fn = fn!=null?fn:function(){};
	this.instance = instance;
	this.appendedArgs = appendedArgs;
}
Callback.prototype.call = function()
{
	var args = new Array(arguments.length);
	for (var i = 0; i < arguments.length; ++i) // sadly, 'arguments' is only an 'Array-like' object
		args[i] = arguments[i];
	this.fn.apply(this.instance?this.instance:this.fn, args.concat(this.appendedArgs));
}
Callback.prototype.apply = function(argArray)
{
	this.fn.apply(this.instance?this.instance:this.fn, argArray.concat(this.appendedArgs));
}
Callback.prototype.toString = function()
{
	return '[Callback; instance=' + this.instance + '; fn=' + this.fn + '; args=' + this.appendedArgs + ']';
}

function AjaxEvent(name, servlet)
{
	this.name = name;
	this.servlet = servlet;
}

// @param serveletURL the base serveletURL
function AjaxServlet(servletURL)
{
//	this.canAjax = true;
	this.servletURL = servletURL;
	this.timeout = 50;
	this.listeners = new Object();
}
AjaxServlet.prototype.addEventListener = function(event, callbackHelper) {
	if (!this.listeners[event]) {
		this.listeners[event] = new Array();
	}
	this.listeners[event].push(callbackHelper);
}
AjaxServlet.prototype.dispatchEvent = function(event) {
	event.servlet = this;
	if (!this.listeners[event])
		return;
	for (var i in this.listeners[event])
		this.listeners[event][i].call(event);
}

// see AjaxServlet.communicate. this is a helper method that puts requests in a timeout
AjaxServlet.prototype.command = function(data, callbackHelper) {
	// these function()-closures expire with the timeout
	var This = this;
	setTimeout(function() {
		This.communicate(data, callbackHelper);
	}, this.timeout);
}

// @param callbackHelper a Callback instance
AjaxServlet.prototype.communicate = function(data, callbackHelper) {
     // TODO: write a test case for this: http://www.jibbering.com/faq/faq_notes/closures.html#clMem
    var state_callback = function () {
        if (xtr.readyState != 4)
             return;
        var status = null;
        try {
        	status = xtr.status;
       	} catch (e) {
       	// RE: NS_ERROR_NOT_AVAILABLE
       	// mozilla, at least, possibly other browsers, throw an exception when accessing this property
       	// if the connection failed under various circumstances
       	// there doesn't seem to be a good way to detect connection failures other than this
       	//
       	// http://groups.google.com/group/mozilla.dev.platform/browse_thread/thread/4dca25a2561a3d78
       	// http://lxr.mozilla.org/mozilla1.8/source/content/base/src/nsXMLHttpRequest.cpp#635
       	//	alert('XMLHttpRequest: connection unknown failure: ' + xtr.servletURL);
       		return 
       	}
        if (status == 200) {
//        	eval(currentFunction + "(xtr.responseXML);");
			callbackHelper.call(xtr.responseXML);
//			ajax.canAjax = true;
        } else {
        	alert('responseText='+xtr.responseText);
//        	ajax.canAjax = true;
        }
	};

    try {
        var xtr = this.getXTR(true);
    	xtr.onreadystatechange = state_callback;
    	state_callback.xtr = xtr;
		xtr.open("POST", this.servletURL, true);
		xtr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
		xtr.setRequestHeader("Content-length", data.length);
		xtr.setRequestHeader("Connection", "close");
	    xtr.send(data); // send(null) causes some issues (NS_ERROR_NOT AVAILABLE in mozilla when accessing request.status ~sometimes~) http://www.thescripts.com/forum/thread436534.html
	} catch (Ex) {
		alert('AjaxCommunicate: ('+Ex.message+');' + Ex);
//		this.canAjax = false;
	}
}

AjaxServlet.prototype.parseContent = function(content_data) {
	var to_eval = "var helperRes = " + content_data + ";\n";
	try {
		eval(to_eval);
	} catch (Ex) {
		alert('exception: ' + content_data);
		setStatus({0: "We couldn't parse: <BR>" + content_data});
		var helperRes = false;
	}
	return helperRes;
}

AjaxServlet.prototype.getXTR = function(need_req_header) {
    var xtr;
    var ex;

    if (typeof(XMLHttpRequest) != "undefined") {
	// The Firefox/Safari/Opera way
        xtr = new XMLHttpRequest();
    } else {
	// The IE way(s)

        try {
            xtr = new ActiveXObject("Msxml2.XMLHTTP.4.0");
        } catch (ex) {
            try {
                xtr = new ActiveXObject("Msxml2.XMLHTTP");
            } catch (ex) {
            }
        }
    }

    if (need_req_header) {
	    // don't work in Opera that only half-supports XMLHttpRequest
	    try {
        	    if (xtr && ! xtr.setRequestHeader)
	            xtr = null;
	    } catch (ex) { }
    }

    return xtr;
}

AjaxServlet.prototype.regEvent = function(target, evt, func) {
    if (! target) return;
    if (target.attachEvent)
        target.attachEvent("on"+evt, func);
    if (target.addEventListener)
        target.addEventListener(evt, func, false);
}


// I rewrote this to suit the way I see these things working
// This takes either an XML String or and XML Dom Document and
// an XPATH String and returns an array of result nodes.
//
// It should work under IE and Mozilla just fine.
AjaxServlet.evalXPath = function(XML, XPATH) {
	var found = [];
	var xmlDoc = AjaxServlet.getXMLDocument(XML);
	if (window.ActiveXObject) {
		var nodelist = xmlDoc.selectNodes(XPATH);
		for(var i = 0; i < nodelist.length; i++) {
			found.push(nodelist.item(i));
		}
	} else {
		var result = xmlDoc.evaluate(XPATH, xmlDoc, null, XPathResult.ANY_TYPE, null)
  		var res = result.iterateNext()
  		while (res) {
    		found.push(res);
    		res = result.iterateNext()
    	}
  	}
	return found;
}

//Sometimes we don't know if we already have the dom, or just a String
//Maybe we just don't care and want to be lazy.
AjaxServlet.getXMLDocument = function(XML) {
	var xmlDoc;
	if (window.ActiveXObject) {
		if(XML instanceof ActiveXObject) {
			xmlDoc = XML;
		} else {
			xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
			xmlDoc.loadXML(XML);
		}
	} else {
		// note: opera doesn't support this
		if(window["XMLDocument"] && XML instanceof XMLDocument) {
			xmlDoc = XML;
		} else {
			var objDOMParser = new DOMParser();
	    	var xmlDoc = objDOMParser.parseFromString(XML, "text/xml");
	    }
	}
	return xmlDoc;
}

/*
	replaces the contents of the passed-in node with the transform
*/
AjaxServlet.replaceNodeByTransform = function(node, XML, XSLTPath){
	var xmlDoc = AjaxServlet.getXMLDocument(XML);
	if(document.implementation && document.implementation.createDocument){
		// Mozilla
	
		var xsltProcessor = new XSLTProcessor();
		
		// load the xslt file
		var myXMLHTTPRequest = new XMLHttpRequest();
		myXMLHTTPRequest.open("GET", XSLTPath, false);
		myXMLHTTPRequest.send(null);
		
		// get the XML document
		var xslStylesheet = myXMLHTTPRequest.responseXML;
		xsltProcessor.importStylesheet(xslStylesheet);
						
		//transform
		var resultDocument = xsltProcessor.transformToFragment(xmlDoc, document);
		while (node.firstChild) node.removeChild(node.firstChild);
		node.appendChild(resultDocument);
		
	}else if(window.ActiveXObject){
		// IE
		// Load XSL
		xsl = new ActiveXObject("Microsoft.XMLDOM");
		xsl.async = false;
		xsl.load(XSLTPath);
		// Transform
		var xform = xmlDoc.transformNode(xsl);
		//var re = new RegExp("^<.xml.*?>(.*)", "gm");
		//xform = xform.replace(re, "$1");
		//alert(xform);
	//	var xformNode = AjaxServlet.getXMLDocument("<?xml version='1.0'?>"+xform);
//		var xformNode = AjaxServlet.getXMLDocument(xform);
		node.innerHTML= xform;
		AjaxServlet.executeScriptsInNodeStr(xform);

		if(xmlDoc.parseError.reason != "") {
			alert('parseError: ' + xmlDoc.parseError.reason);
			alert('parseError: ' + xmlDoc.parseError.srcText);
		}
	}else{
		// Browser unknown
		alert("Browser unknown");
	}
}


// Yoinked from:
// http://www.w3schools.com/dom/loadxmldoc.asp
// Sensible, I think the async thing makes it so it'll block on the load. Thank goodness!
AjaxServlet.prototype.loadXMLDoc = function(dname) {
	var xmlDoc;
	// code for IE
	if (window.ActiveXObject) {
		xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
	}
	// code for Mozilla, Firefox, Opera, etc.
	else if (document.implementation && document.implementation.createDocument) {
		xmlDoc=document.implementation.createDocument("","",null);
	} else {
		alert('Your browser cannot handle this script');
	}
	xmlDoc.async=false;
	xmlDoc.load(dname);
	return(xmlDoc);
}

//A method to run generated SCRIPTS in IE. Use this
// when you've generated HTML and appended it somewhere
// in your document, but it contains scripts that you
// would expect to run. You're out of luck on document.write,
// but if you've got this far, I imagine you can sort out an
// alternative to that readily.
//
//@param node The node under which to look for SCRIPT elements
// you may have thought you called them "script" and so forth,
// but IE probably borked it all up for you. 
AjaxServlet.executeScriptsInNode = function(node) {
	var scriptnodes = node.getElementsByTagName("SCRIPT");
	for(var i = 0; i < scriptnodes.length; i++) {
//	alert(scriptnodes[i].innerHTML);
		eval(scriptnodes[i].innerHTML);
	}
}
AjaxServlet.executeScriptsInNodeStr = function(node) {
	var nodel = node.toLowerCase();
	var index = 0;
	var index1;
	var index2;
	while ((index = nodel.indexOf("<script", index2))!=-1) {
		index1 = nodel.indexOf(">", index);
		index2 = nodel.indexOf("</script>", index);
		var script = node.substring(index1+1, index2);
//		alert(script);
		eval(script);
	}
}


//A method that sets all the labels for indicated form fields to
//a yellow highlight.
AjaxServlet.prototype.notifyAboutFormFields = function(notifyFieldArray, xml_attribute, tag_name) {
 var toFix = document.getElementsByName(tag_name);
 for(var i = 0; i < toFix.length; i++) {
  toFix[i].style.backgroundColor = "transparent";
  for(var j = 0; j < notifyFieldArray.length; j++) {
   if(notifyFieldArray[j].getAttribute(xml_attribute) == toFix[i].id) {
    toFix[i].style.backgroundColor = "yellow";
    break;
   }   
  }
 }
}

// browser compat for textContent/innerText text nodes
AjaxServlet.prototype.getTextContent = function(node)
{
	if (node.textContent)// maybe we should just skip this step
		return node.textContent;
	if (node.firstChild && node.firstChild.nodeType==3)
		return node.firstChild.nodeValue;
	return null;
}
// recurses until the first text (or CDATA) node and returns its value
//function getTextContent(node)
//{
//alert(node.nodeName + ';' + node.nodeType + ';' + node.nodeValue);
//	var TEXT_NODE = 3;
//	var CDATA_NODE = 4;
//	if (node.nodeType == TEXT_NODE | node.nodeType == CDATA_NODE)
//		return node.nodeValue;
//	if (node.firstChild)
//		return getTextContent(node.firstChild);
//	return null;
//}

AjaxServlet.xmlToString = function(xml)
{
	if (window.ActiveXObject) {
		if (xml.innerHTML)
			return xml.innerHTML;
		if (xml.xml)
			return xml.xml;
		alert('dont know how to serialize: ' + xml);
//		return xml.transformNode(document.createElement('dummy'));
	} else if (window.XMLSerializer) {
		var string = (new XMLSerializer()).serializeToString(xml);
		return string;
	} else {
		alert('no serializer available');
	}
}
