
/*
	Author: Stephen W. Cote
	Email: wranlon@hotmail.com
	
	Copyright 2002, All Rights Reserved.
	
	Do not copy, archive, or distribute without prior written consent of the author.
	
	Version: SPEC-1.0.39.02.03.2003
*/

/*
	12/28/2002
		Fixed bug in returnXmlHttpObjectToPool; this was causing a monster headache!
*/

/*

	Usage:
		Synchronous GET:
			[xml_dom_object] = getXml(path);
		Asynchronous GET:
			[int] = getXml(path,handler,1,{optional_id});
		Cached Asynchronous GET:
			[int] = getXml(path,handler,1,request_id,1);
		Synchronous POST:
			[xml_dom_object] = postXml(path,data);
		Asynchronous POST:
			[int] = postXml(path,data,handler,1,{optional_id});

*/


/*
	the org, org.cote, and org.cote.js defs are a literal translation from the Engine project.
	The structure is left intact for compatibility.
*/

var org = {};
org.cote = {};
org.cote.js = {};

org.cote.js.xml = {
	object_version:"SPEC-1.0.39.02.03.2003",
	counter:0,
	_xml_requests:[],
	_xml_requests_map:[],
	_xml_http_objects:[],
	_xml_http_object_use:0,
	_xml_http_object_count:5,
	_xml_http_object_pool_size:5,
	_xml_http_object_pool_max:10,
	
	/* notate whether the pool was created */
	_xml_http_pool_created:0,
	/* notate whether the pool was created */
	_xml_http_pool_enabled:1,

	newXmlDocument:function(n){
		/*
			n = "root node";
		*/
		
		var r = 0,e;
		if(typeof document.implementation != "undefined" && typeof document.implementation.createDocument != "undefined"){
			r = document.implementation.createDocument("",n,null);
		}
		else if(typeof ActiveXObject != "undefined"){
			r = new ActiveXObject("MSXML.DOMDocument");
			e = r.createElement(n);
			r.appendChild(e);
		}
		else{
			/* ... */
		}
		return r;
	},
	clearCache:function(){
		var _x = org.cote.js.xml,i = 0,o;
		for(;i<_x._xml_requests.length;i++){
			o = _x._xml_requests[i];
			if(o.cached && typeof o.cached_dom == "object"){
				o.cached_dom = 0;
			}
		}
	},
	resetXmlHttpObjectPool:function(){
		var _x = org.cote.js.xml,i = 0,o;
		_x._xml_http_pool_created = 1;
		_x._xml_http_object_use=0;
		_x._xml_http_objects=[];
		_x._xml_http_object_count = _x._xml_http_object_pool_size;
		for(;i < _x._xml_http_object_pool_size; i++){
			o = _x._xml_http_objects[i] = _x.newXmlHttpObject(1,i);
		}
	},

	newXmlHttpObject:function(b,i){
		/*
			b = return a hash for use with pooling
			i = pool index value.  b must be true for i to be used
		*/
		var o = null,v,f;
		if(typeof XMLHttpRequest != "undefined"){
			o = new XMLHttpRequest();
		}
		else if(typeof ActiveXObject != "undefined"){
			try{
				o = new ActiveXObject("MSXML2.XMLHTTP.3.0");
			}
			catch(e){
				alert("XMLError: " + (e.description?e.description:e.message));
			}
		}
		if(b && typeof i == "number"){
			v= {
				xml_object:o,
				in_use:0,
				use_count:0,
				index:i,
				/* vid = variant id */
				vid:-1,
				handler:0
			};

			try{
				if(typeof XMLHttpRequest != "undefined" && o instanceof XMLHttpRequest){
					v.handler = function(){org.cote.js.xml._handle_xml_request_load(i);};
					o.addEventListener("load",v.handler,false);
				}
				else if(typeof ActiveXObject != "undefined" &&  o instanceof ActiveXObject){
					v.handler = function(){org.cote.js.xml._handle_xml_request_readystatechange(i);};
					/*
						Can't attach an event to this object with attachEvent
					*/
					o.onreadystatechange=v.handler;
				}
			}
			catch(e){
				alert("Error in _request_xmlhttp: " + (e.description?e.description:e.message));
			}				

			return v;
		}
		else{
			return o;
		}
		
	},

	returnXmlHttpObjectToPool:function(i){
		var _x = org.cote.js.xml,b=0,o,a;
		a = _x._xml_http_objects;

		if(typeof a[i] == "object"){
			o = a[i];
			if(o.index >= _x._xml_http_object_pool_size){
				a[i] = null;
			}
			else{
				o.in_use = 0;
				o.vid = -1;
				/*
					Just for ActiveXObject MSXML.
				*/
				o.xml_object.abort();
				if(typeof ActiveXObject != "undefined" && o.xml_object instanceof ActiveXObject){
					o.xml_object.onreadystatechange = o.handler;
				}
				
			}
			_x._xml_http_object_use--;
		}
		return 1;
	},
	
	getXmlHttpObjectFromPool:function(){
		var _x = org.cote.js.xml,i = 0,b=0,o,a,n=-1;

		if(!_x._xml_http_pool_created) _x.resetXmlHttpObjectPool();
		a = _x._xml_http_objects;		
		for(;i<a.length;i++){
			if(typeof a[i] == "object" && typeof a[i].in_use == "number" && !a[i].in_use){
				a[i].in_use = 1;
				b = i;
				break;
			}
			/* mark the next known null marker for re-use*/
			if(n == -1 && a[i] == null){
				n = i;
			}
		}

		if(b < 0){
			b = (n > -1)?n:a.length;
			if(b < _x._xml_http_object_pool_max){
				a[b] = _x.newXmlHttpObject(1,b);
				a[b].in_use = 1;
			}
			else{
				alert("Max pool size reached!");
				b = 0;
			}
		}

		if(b > -1){
			_x._xml_http_object_use++;
			return a[b];
		}
		
		return null;

	},
	
	serialize:function(n){
		var v;
		if(typeof XMLSerializer != "undefined"){
			return (new XMLSerializer()).serializeToString(n);
		}
		else if(typeof n.xml == "string"){
			return n.xml;
		}
	},
	getCDATAValue:function(n){
		var c,d="",i=0,e;
		c = n.childNodes;
		for(;i<c.length;i++){
			e=c[i];
			if(e.nodeName=="#cdata-section") d+=e.nodeValue;
		}
		return d;
	},
	
	selectSingleNode:function(d,x,c){
		/*
			d = XmlDocument
			x = xpath
			c = context node
		*/
		var s,i,n;
		if(typeof d.evaluate != "undefined"){
			c = (c ? c : d.documentElement);
			s = d.evaluate(x,c,null,0,null);

			return s.iterateNext();
		}
		else if(typeof d.selectNodes != "undefined"){
			return (c ? c : d).selectSingleNode(x);
		}

		return 0;
	
	},
	selectNodes:function(d,x,c){
		/*
			d = XmlDocument
			x = xpath
			c = context node
		*/
		var s,a = [],i,n;
		if(typeof d.evaluate != "undefined"){
			c = (c ? c : d.documentElement);
			s = d.evaluate(x,c,null,0,null);

			n = s.iterateNext();

			while( typeof n == "object" && n != null){
				a[a.length] = n;
				n = s.iterateNext();
			}

			return a;

		}

		else if(typeof d.selectNodes != "undefined"){
			return (c ? c : d).selectNodes(x);
		}

		return a;
	
	},
	
	queryNodes:function(x,p,n,a,v){
		return org.cote.js.xml._queryNode(x,p,n,a,v,1);
	},
	queryNode:function(x,p,n,a,v){
		return org.cote.js.xml._queryNode(x,p,n,a,v,0);
	},
	_queryNode:function(x,p,n,a,v,z){
		/*
			 x = xdom
			 p = parent path
			 n = node path
			 a = attribute name
			 v = attribute value
		*/

		var i=0,b,e,c,r=[];
		if(!z) r = null;
		
		c = x.getElementsByTagName(p);
		
		if(typeof n=="string"){

			if(!c.length){
				if(!z) return null;
				else return r;
			}
			c = c[0]; 
			e = c.getElementsByTagName(n);
		}
		else e = c;

		
		for(;i<e.length;i++){
			b = e[i];
			if((!a && !v) || (b.getAttribute(a) == v)){
				/*
					single query
				*/
				if(!z){
					r = b;
					break;
				}

				else r[r.length]=b;
				
			}
		}
		return r;
	},

	_handle_xml_request_load:function(xml_id){
		var _x=org.cote.js.xml,o,v,z;
		try{

			if(_x._xml_http_pool_enabled && typeof _x._xml_http_objects[xml_id] == "object"){
				z = _x._xml_http_objects[xml_id].vid;
				if(z == -1){
					alert("invalid  pool index for " + xml_id);
					return 0;
				}
				xml_id = z;
			}

			if(typeof _x._xml_requests_map[xml_id] == "number"){
				o = _x._xml_requests[_x._xml_requests_map[xml_id]];
				
				v = {"xdom":null,"id":xml_id};
				if(o.obj != null && o.obj.responseXML != null){
					v.xdom = o.obj.responseXML;
				}
				else if(o.obj != null){
					alert("Error loading '" + o.url + "'. Response text is: " + o.obj.responseText);
				}
				else{
					alert("Error loading '" + o.url + "'. The internal XML object reference is null");			
				}
	
				o.completed = 1;
			
				if(o.internal_handler){
					o.internal_handler = 0;
				}

				if(o.cached){
					o.cached_dom = v.xdom;
				}

				/*org.cote.js.message.MessageService.publish("onloadxml",v);*/
				if(typeof o.handler=="function") o.handler("onloadxml",v);
	
				/*
					clear out the request object
				*/
				if(o.pool_index > -1){
					_x.returnXmlHttpObjectToPool(o.pool_index);
				}
				
				o.obj = 0;
	
			}
			else{
				alert("Invalid id reference: " + xml_id);
			}

		}
		catch(e){
			alert("Error in handle_xml_request_load: " + (e.description?e.description:e.message));
		}
	},

	_handle_xml_request_readystatechange:function(xml_id){
		var _x=org.cote.js.xml,o;

		/*
			Slightly different behavior for pooled requests and non-pooled requests.
			
			Ultimately, the issue IE won't detach the onreadystate event handler, so the index into the pool is passed
			instead of the unique request id.  This should actually be better anyway as it removes the need to constantly 
			attach and detach event handlers on the pooled objects.
		*/
		if(_x._xml_http_pool_enabled && typeof _x._xml_http_objects[xml_id] == "object"){
			o = _x._xml_http_objects[xml_id];
			if(typeof o.xml_object == "object" && o.xml_object.readyState == 4){
				_x._handle_xml_request_load(xml_id);
			}
		}
		else if(typeof _x._xml_requests_map[xml_id] == "number"){
			o = _x._xml_requests[_x._xml_requests_map[xml_id]];
			if(typeof o.obj == "object" && o.obj.readyState == 4){

				_x._handle_xml_request_load(xml_id);
			}
		}
	},

	/*
		getXml(path,handler,async,id,cached);
		
		id is optional.  Use this where two or more xml transactions will be directed
		through the same handler.
	*/
	getXml:function(p,h,a,i,c){
	/*
			p = path
			h = handler
			a = async
			i = id
			c = cached
	*/
		return org.cote.js.xml._request_xmlhttp(p,h,a,i,0,null,c);
	},

	/*
		postXml(path,data,handler,async,id);
		handler is optional for synchronous requests
		to specify a custom id for synchronous requests, use null or 0 for the handler, and false or 0 for the async property.
		id is optional.
		
		Note that postXml assumes no caching.
	*/
	postXml:function(p,d,h,a,i){
		/*
			Caching is not provided for the postXml wrapper.
		*/
		return org.cote.js.xml._request_xmlhttp(p,h,a,i,1,d,0);
	},
	/*
		_request_xml is asynchronous.
	*/
	_request_xmlhttp:function(p,h,a,i,x,d,c){
		/*
			p = path
			h = handler
			a = async
			i = id
			x = is_post as bool
			d = data as string or DomDocument
			
			r = pool/new x obj
			b = bool
			
			c = cache result
		*/
		
		var _x=org.cote.js.xml,f,o=null,v,y,z,r,b,b_ia,g;

		if(typeof p != "string" || p.length == 0){
			alert("Invalid path parameter in _request_xmlhttp");
			return 0;
		}
		
		if(typeof c=="undefined") c = 0;
		if(typeof x=="undefined") x = 0;
		if(typeof d=="undefined") d = null;

		z = (x?"POST":"GET");

		// make up a unique id if one isn't provided
		if(typeof i!="string") i="uid-" + (++_x.counter) + "-" + parseInt(Math.random()*10000);
		
		/* check for a cached instance */
		
		if(
			typeof _x._xml_requests_map[i] == "number"
			&&
			typeof _x._xml_requests[_x._xml_requests_map[i]] == "object"
		){
			r = _x._xml_requests[_x._xml_requests_map[i]];
			if(r.cached && typeof r.cached_dom == "object"){
				b = {"xdom":r.cached_dom,"id":i};				
				_m.publish("onloadxml",b);
				if(typeof h == "function") h("onloadxml",b);
				return 1;
			}
		}
		
		
		/* get a new XML object, or a pooled object depending on the settings */
		b = _x._xml_http_pool_enabled;
		if(b){
			r = _x.getXmlHttpObjectFromPool();
		}
		else{
			r = _x.newXmlHttpObject();
		}

		/*
			Unable to obtain an XML object, so bail out.
		*/
		if(!r){
			alert("Null XML object in in _request_xmlhttp.");
			return 0;
		}

		/* update the pool id reference, if pooling is enabled */
		if(b) r.vid = i;
		
		y = _x._xml_requests.length;
		_x._xml_requests[y] = {url:p,id:i,obj:(b?r.xml_object:r),internal_handler:0,handler:h,method:(x?1:0),completed:0,pool_index:(b?r.index:-1),cached:c,cached_dom:0};
		_x._xml_requests_map[i]=y;
		o = _x._xml_requests[y].obj;

		if(!p.match(/:\/\//)){
			var m,e=new RegExp("^/");
			if(!p.match(e)){
				m=location.pathname;
				m=m.substring(0,m.lastIndexOf("/")+1);
				p=m + p;
			}
			p=location.protocol + "//" + location.host + p;
		}
		_x._xml_requests[y].url = p;

		/*
			Add event handlers based on instance of XML object
			
			Must check for typeof object before instanceof or IE will bomb out.
			
			Check for:
				a) this is not a pooled request; pooled requests use index->id maps
				b) this as an async request
				c) the type of request object
		*/
		b_ia = (typeof ActiveXObject != "undefined" &&  o instanceof ActiveXObject)?1:0;
		try{
			if(!b && a && typeof XMLHttpRequest != "undefined" && o instanceof XMLHttpRequest){
				_x._xml_requests[y].internal_handler = function(){org.cote.js.xml._handle_xml_request_load(i);};
				o.addEventListener("load",_x._xml_requests[y].internal_handler,false);
			}
			else if(!b && a && b_ia){
				_x._xml_requests[y].internal_handler = function(){org.cote.js.xml._handle_xml_request_readystatechange(i);};
				/*
					Can't attach an event to this object with attachEvent
				*/
				o.onreadystatechange=_x._xml_requests[y].internal_handler;
			}
		}
		catch(e){
			alert("Error in _request_xmlhttp: " + (e.description?e.description:e.message));
		}		

		
		/*
			There is a problem with the ActiveXObject not liking the multiple levels of object references,
			particularly the embedded array reference into the http_objects array used for pooling.
			The good news is this only seems to apply to syncronous requests, and only for ActiveXObjects.
			The bad news is fixing the problem requires the following:
				- change the onreadystatechange handler to point elsewhere.  Since this can't be null, its pointed at an empty stub function.
				- set the array reference for http_objects to null.
			At this point, those referencers are:
		*/
		if(b && !a && b_ia){
			o.onreadystatechange = _x._stub;
			_x._xml_http_objects[_x._xml_requests[y].pool_index] = null;
		}
		
		g = (a?true:false);
		o.open(z,p,g);
		o.send(d);
		if(!a){
			/*
				Don't use the reference in the requests array here because it loses context when he open and send methods are invoked.
				Instead, since this is synchronous, just use 'o'
				
				This is symptomatic of the IE problem mentioned above, but the root cause seems to be
				the manner in which the ActiveXObject is stored in the array.
				
				The issue manifests as querying responseXML throws an error, or undefined and the object references get out of sync.
				This can be tested by comparing o == _x._xml_requests[y].obj
				
				With the IE fix in place, the test is true.
				Mozilla is false, but that isn't an issue (right now) because the response can still be obtained.
			*/

			z = o.responseXML;

			/*
				Now that the request has been made, time for part 2 of the IE Synchronous problem.
				Reset the pool object (where the xml_object property contains the XMLHTTPRequest object) to the array.
				Also, manually invoke the _handle_xml_request_load method
			*/
			if(b && b_ia){
				_x._xml_http_objects[_x._xml_requests[y].pool_index] = r;
				_x._handle_xml_request_load(_x._xml_requests[y].pool_index);
			}

			_x._xml_requests[y].obj = null;
			
			if(!b && _x._xml_requests[y].pool_index > -1){
				_x.returnXmlHttpObjectToPool(_x._xml_requests[y].pool_index);
			}
			
			return z;
		}
		return 1;
	},
	_stub:function(){
		/* do nothing */
	},

	getInnerText:function(s){
		var r = "",a,i,e;
		if(typeof s == "string") return s;
		if(typeof s=='object' && s.nodeType==3) return s.nodeValue;
		if(s.hasChildNodes()){
			a = s.childNodes;
			for(i=0;i<a.length;i++){
				e = a[i];
				if(e.nodeType==3) r+=e.nodeValue;
				if(e.nodeType==1 && e.hasChildNodes()){
					r+=this.getInnerText(e);
				}
			}
		}
		return r;
	},
	removeChildren:function(o){
		var i;
		for(i=o.childNodes.length-1;i>=0;i--){
			o.removeChild(o.childNodes[i]);
		}
	},
	setInnerXHTML:function(t,s,p,d,z){
		/*
			t = target
			s = source
			p = preserve
			d = target document object
			z = no recursion
			
			r = return node reference
		*/
		var y,e,a,l,x,n,v,r = 0,b;
		
		/* typeof d == DATATYPES.TYPE_UNDEFINED */
		if(!d) d = document;
		
		b = (d == document?1:0);
		
		if(!p){
			org.cote.js.xml.removeChildren(t);
		}

		y=(s && typeof s=="object")?s.nodeType :(typeof s=="string")?33:-1;
		switch(y){
			case 1:
				e=d.createElement(s.nodeName);
				a=s.attributes;
				l=a.length;
				for(x=0;x<l;x++){
					n=a[x].nodeName;
					v=a[x].nodeValue;

					/*
						Must check for d == document (b) to make sure whether this is the HTML DOM or not, because
						these cases only apply to IE oddness in the HTML DOM, not the XML DOMs.
					*/
					/* stupid IE */
					if(b && n=="style"){
						e.style.cssText=v;
					}
					/* stupid IE */
					else if(b && n=="id"){
						e.id=v;
					}
					/* stupid IE */

					else if(b && n=="class"){
						e.className=v;
					}

					/* stupid IE */
					else if(b && n.match(/^on/i)){
						eval("e." + n + "=function(){" + v  +"}");
					}
					else{
						e.setAttribute(n,v);
					}
				}
	
				if(!z && s.hasChildNodes()){
					a=s.childNodes;
					l=a.length;
					for(x=0;x<l;x++){
						this.setInnerXHTML(e,a[x],1,d);
					}
				}
				t.appendChild(e);
				r = e;
				break;
			case 3:
				e=s.nodeValue;
				if(e){
					e=e.replace(/\s+/g," ");
					t.appendChild(d.createTextNode(e));
					r = e;
				}
				break;
			case 8:
				/*
					Ignore comments
				*/
				break;
			case 33:
				e=s;
				if(e){
					e=e.replace(/^\s*/,"");
					e=e.replace(/\s*$/,"");
					e=e.replace(/\s+/g," ");
					t.appendChild(d.createTextNode(e));
					r = e;
				}
				break;
			default:
				break;
		}
		return r;
	}
};
