﻿// JScript File
if (!d3) throw( "d3 object has not been instantiated! d3.base.js should precede this script." );
if (typeof( d3.xml ) != "undefined") throw( "d3.xml functionality has already been included!  review script order." );

d3.xml = // xml is an object property of d3, not a class
{
	_about : "d3methods xml helper object, v1.2.002, 4/29/2006",
	_log : d3.log,

	READY_STATE_UNINITIALIZED : 0,
	READY_STATE_LOADING : 1,
	READY_STATE_LOADED : 3,
	READY_STATE_INTERACTIVE : 3,
	READY_STATE_COMPLETE : 4
};

/* add .xml for Mozilla */
if (d3.env.IS_MOZILLA && !Node.prototype.xml)
{  
	Node.prototype.__defineGetter__("xml", 
		function()
		{ return (new XMLSerializer).serializeToString( this ); } 
	);
}

d3.xml.about = d3.about;

d3.xml.setLog = function (log)
{
	if (log instanceof (d3.Logger)) this._log = log;
};

if (d3.env.HAS_ACTIVEX && typeof( XMLHttpRequest ) == "undefined")
{
	DEBUG$( "defining XMLHttpRequest" );
	XMLHttpRequest = function()
	{
		return d3._getVersionedActiveX( [ "MSXML2.XMLHTTP.5.0", "MSXML2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP" ] );
	};
}

DOMDocument = function() { return null; }
if (d3.env.HAS_ACTIVEX)
{
	d3.xml._getDOMDocument = function( freeThreaded )
	{
		var a = [ "MSXML2.DOMDocument.5.0", "MSXML2.DOMDocument.4.0", "MSXML2.DOMDocument.3.0" ];
		if (d3.env.IS_ASP || freeThreaded )
			a = [ "MSXML2.FreeThreadedDOMDocument.5.0", "MSXML2.FreeThreadedDOMDocument.4.0", "MSXML2.FreeThreadedDOMDocument.3.0" ];
		return d3._getVersionedActiveX( a );
	};
	DEBUG$( "defining DOMDocument (MSXML)" );
	DOMDocument = function( pathOrText, isText, freeThreaded )
	{
		DEBUG$( "DOMDocument( "+ String.truncate( pathOrText, 72 )+ ", "+ isText + ", "+ freeThreaded + " )" );
		var doc = d3.xml._getDOMDocument( freeThreaded );
		if (typeof(pathOrText) == "undefined") return doc;
		
		if (typeof(isText) == "undefined")
		{
			if (typeof(pathOrText) == "string")
				isText = pathOrText.isMarkup();
			else
				isText = false;
		}
		if (isText)
			doc.loadXML( pathOrText );
		else
			doc.load( pathOrText );
		return doc;
	}
}
else if (d3.env.IS_MOZILLA)
{
	d3.xml._loadDocFromText = function( doc, text )
	{
		if (d3.env.HAS_ACTIVEX) 
			doc.loadXML( text );
		else if (d3.env.IS_MOZILLA)
		{
			var frag = (new DOMParser()).parseFromString( text, "text/xml" );
			while (doc.hasChildNodes())
				doc.removeChild( doc.lastChild );
				
			for (var k = 0; k < frag.childNodes.length; k++)
				doc.appendChild( doc.importNode( frag.childNodes[k], true ) );	
		}
		return doc;
	};

	DEBUG$( "defining DOMDocument (Mozilla)" );
	DOMDocument = function( pathOrText, isText )
	{
		var doc = document.implementation.createDocument( "", "xml", null );
		if (typeof(isText) == "undefined")
		{
			if (typeof(pathOrText) == "string")
				isText = pathOrText.isMarkup();
			else
				isText = false;
		}
		if (isText)
			doc = d3.xml._loadDocFromText( doc, pathOrText );
		else
			doc.load( pathOrText );
		return doc;
	};
}

XSLDocument = function( pathOrText, isText )
{
	return DOMDocument( pathOrText, isText, true );
}

Object.prototype.toQueryString = function () 
{	
	var a = new Array;
	if (this instanceof Array)
	{
		for (var k = 0; k < this.length; k++)
			a.push( escape(this[k]) );
	}
	else
	{
		for (var x in this)
		{
			var item = this[x];
			if (typeof(item) == "function") continue;
			a.push( x + "="+ escape(this[x]) );
		}
	}
	if (a.length < 1) return "";
	return "?"+ a.join("&");
};

d3.xml.formToXml = function( form )
{
	var a = new Array();
	var fields = form.getElementsByTagName( "input" );
	for (var k = 0; k < fields.length; k++)
	{
		var f = fields[k];
		var type = d3.ifNot( f.type, "" ).toLowerCase();
		if (type == "button") continue;
		var n = d3.ifNot( f.id, d3.ifNot( f.name, "item" ) );
		var s = f.value;
		if (type == "checkbox") s = f.checked.toString();
		a.push( d3.xml.makeElement( n, "", s ) );
	}
	var body = a.join( "" );
	var a = new Array();
	if (form.id) a.push( d3.xml.makeAttribute( "id", form.id ) );
	if (form.name) a.push( d3.xml.makeAttribute( "name", form.name ) );
	return d3.xml.makeElement( "form", a.join( "" ), body );
};

d3.HttpRequest = function( url, options )
{
	DEBUG$( "d3.HttpRequest options = "+  d3.about( "", options, true ) );
	if (!options) options = { async: false, method: "get" };
	var async = (options.async == true);
	var method = new String(options.method);
	var body = null;
	method = (method.toLowerCase() == "post") ? "post" : "get";
	var fComplete = ( typeof( options.oncomplete ) == "function" ) ? options.oncomplete : null ;
	var xh = new XMLHttpRequest();
	if ( fComplete ) 
	{
		xh.onreadystatechange = function() 
		{
			if (xh.readyState == d3.xml.READY_STATE_COMPLETE)
				fComplete( { XMLHttpRequest : xh, 
								XMLDocument : xh.responseXML, 
								url : url } ); 
		}
	}
	if (options.parameters)
	{
		var qs = options.parameters.toQueryString();
		if (method == "get") 
			url = url + qs;
		else
			body = qs;
	}

	var username = (options.username ? options.username : "" );
	var password = (options.password ? options.password : "" );
	DEBUG$( "XMLHttpRequest.open( "+ method + ", "+ url + " , "+ async + ", "+ username + ", "+ password + " )" );
	xh.open( method, url, async ); //, username, password );

	if (options.headers && options.headers.length > 0 )
	{
		LOG$( "headers: "+ options.headers.length );
		for (var e = new d3.Enumerator(options.headers); !e.atEnd(); e.moveNext())
		{	
			var h = new d3.NameValue( e.item() );
			LOG$( "header "+ h + ";" );
			xh.setRequestHeader( h.name, h.value );
		}
	
	}
	if (options.XMLDocument)
	{
		body = options.XMLDocument.xml;
	}
	else if (options.body)
	{
		if (typeof(options.body) != "string") throw "Body must be text/xml - " + typeof( options.body );
		body = options.body;
	}
	if (options.cookies && options.cookies.length > 0)
	{
		for (var e = new d3.Enumerator( options.cookies ); !e.atEnd(); e.moveNext())
		{
			s = new String(e.item());
			if (s.substr( 0, 4).toLowerCase() == "Set-") s = substr( 4 );
			xh.setRequestHeader( "Cookie", s );
		}
	}
	xh.setRequestHeader( "Content-Length", (d3.ifNot(body,"")).length );
	xh.send( body );
	return { XMLHttpRequest: xh, XMLDocument: xh.responseXML };
};

d3.xml.getHttpCookies = function( obj )
{
	var xh;
	if (typeof(obj.getAllResponseHeaders) != "unknown") 
		xh = obj;
	else if (obj.XMLHttpRequest)
		xh = obj.XMLHttpRequest;
	
	var a = xh.getAllResponseHeaders( ).split("\n");
	var cookies = new Array();
	for (var k = 0; k < a.length; k++)
		if (a[k].toLowerCase().contains( "$set-coookie" )) cookies.push( a[k].substr( 0, 4 ) );
	return cookies;
};


d3.xml.setSelectionNamespaces = function( xmlDoc )
{
	var nl = new d3.xml.NamespaceList( xmlDoc );
	var s = "";
	for (var i = 0; i < nl.array.length; i++)
	{
		var x = nl.array[i];
		s += "xmlns" + (x.prefix == "" ? "" : ":") + x.prefix + "='"+ x.uri + "' \n";
	}
	//this._log.append( "namespaces: " + s, d3.LOG_DEBUG );
	if (s.length > 0) xmlDoc.setProperty( "SelectionNamespaces", s );
};

d3.xml.NamespaceList = function( xmlDoc )
{
	var aNS = new Array();
	for (var k = 0; k < xmlDoc.documentElement.attributes.length; k++)
	{
		var a = xmlDoc.documentElement.attributes[k];
		var s = new String(a.nodeName); 
		if (s.indexOf( "xmlns" ) == 0) 
			aNS.push( { prefix : s.substr( 6 ), uri : a.nodeValue, substitute : "" } );
	}
	this.array = aNS;

	this.findPrefixFor = function ( uri )
	{
		for (var k = aNS.length - 1; k >= 0; k--)
			if (aNS[k].uri == uri) return aNS[k].prefix;
		return null;
	}

	this.findUriFor = function ( prefix )
	{
		for (var k = aNS.length - 1; k >= 0; k--)
			if (aNS[k].prefix == prefix) return aNS[k].uri;
		return null;
	}

	this.setSubstituteFor = function ( prefix, substitute )
	{
		for (var k = aNS.length - 1; k >= 0; k--)
			if (aNS[k].prefix == prefix) 
			{
				aNS[k].substitute = substitute;
				aNS[k]._re = new RegExp( aNS[k].substitute + ":", "g" );
				return true;
			}
		return false;
	}

	this.translate = function ( xpath )
	{
		var s = xpath;
		for (var k = aNS.length - 1; k >= 0; k--)
			if (aNS[k]._re) s = s.replace( aNS[k]._re, aNS[k].prefix + ":" );
		return s;
	}
};

d3.xml.NamespaceList.prototype.selectSingleNode = function ( node, xpath )
{
	return node.selectSingleNode( this.translate( xpath ) );
};

d3.xml.NamespaceList.prototype.selectNodes = function ( node, xpath )
{
	return node.selectNodes( this.translate( xpath ) );
};


d3.xml.xmlDateTime = function ( dt )
{
	var xdt;
	if ((!dt) || dt == "") 
		xdt = new Date( );
	else
		xdt = new Date( dt );
	return xdt.getFullYear().toString() + "-" + 
		d3.zeroPad(xdt.getMonth() + 1, 2) + "-" +
		d3.zeroPad(xdt.getDate(), 2) + "T" +
		d3.zeroPad(xdt.getHours(), 2) + ":" +
		d3.zeroPad(xdt.getMinutes(), 2) + ":" +
		d3.zeroPad(xdt.getSeconds(), 2)
};

d3.xml.makeAttribute = function ( attribute, value )
{
	if (value == null || value === undefined) return "";
	var s = (new String(value)).escapeXml();
	if (s.length < 1) return "";
	return attribute + "=\""+ s + "\" ";
};

d3.xml.openElement =	function ( tag, attributes )
{
	var s = "";
	if (attributes !== undefined) s = attributes;
	return "<"+ tag + " "+ s + ">";
};
	
d3.xml.closeElement = function ( tag )
{
	return "</"+ tag + ">";
};
	
d3.xml.makeElement = function ( tag, attributes, body )
{
	var attr = "";
	if (attributes !== undefined) attr = attributes;
	if (typeof(body) == "undefined") return "<"+ tag + " "+ attr + "/>";
	var s = body;
	if (typeof(body) != "string") s = new String(body);
	return this.openElement( tag, attr ) 
		+ (s.isMarkup() ? s : s.escapeXml())
		+ this.closeElement( tag );
};

d3.xml.isXmlText = function( text )
{
	return (this.getRootElement( text ) != null);
};

String.prototype.isMarkup = function( )
{
	if (this.indexOf( "<?xml" ) == 0) return true;
	var lt = this.search( /<\s*[\w\:\-]+/ );
	var gt = this.search( />/ );
	return (lt > -1 && lt < gt );
};

Object.fromXmlElement = function( element, recurse )
{
	if (!element || !element.attributes || !element.attributes.length) return null;
	var o = new Object();
	for (var k = 0; k < element.attributes.length; k++)
	{
		var a = element.attributes[k];
		if (!a.nodeName) return null;
		o[ a.nodeName ] = a.nodeValue;
	}
	o['name()'] = element.nodeName;
	o['.'] = element;
	if (recurse == true)
	{
		for (var k = 0; k < element.childNodes; k++)
		{
			var c = element.childNodes[k];
			o[c.nodeName] = Object.fromXmlElement( c );
		}
	}
	return o;	
};

Object.prototype.toXmlElement = function( name )
{
	var a = new Array();
	for (var k in this)
	{
		var x = this[k];
		if (typeof(this[k]) == "function") continue;
		a.push( d3.xml.makeAttribute( k, x ) );
	}
	
	return d3.xml.makeElement( d3.ifNot( name, typeof(this) ), 
		a.join(""), 
		typeof(this) == "object" ? "" : this.toString() ); 
}

d3.xml.getRootElement = function( xmlText, stripTag )
{
	// find outer Xml element and optionally strips opening and closing tag.  
	// xmlText must have opening and closing tag.  invalid
	// xmlText returns null.  empty tags (e.g.: <br/>) return empty string "".
	// example: 
	//		xmlText = "<?xml version> <puff>a <i>magic</i> dragon</puff>" 
	//		return = "a <i>magic</i> dragon"
	//
	var firstTag = xmlText.search( /<\s*[\w\:\-]+/ );		// first semi-valid xml tagname (skips processing instructions, doctypes, comments, etc.)
	if (firstTag < 0) return null;		// no valid tag found

	var firstGT = xmlText.indexOf( ">", firstTag );
	if (firstGT < 0) return null;			// no closing > found

	var tag = xmlText.substring( firstTag + 1, firstGT );
	var tagWords = tag.split( /\s/ );
	var lastWord = "", tagName = "";
	for (var k = 0; k < tagWords.length; k++)
	{
		if (tagWords[k].length > 0)
		{
			lastWord = tagWords[k];
			if (tagName == "") tagName = lastWord;
		}
	}

	if (tagName == "") return null;		// blank tag (e.g.: < >)
	if (lastWord.substr( lastWord.length - 1 ) == "/") 
		if (stripTag) return ""; else return tag;
	
	var re = new RegExp( "<\\s*/\\s*"+ tagName + "\\s*>\\s*$" );
	var closeTag = xmlText.search( re );
	if (closeTag < 0) return null;		// no closing tag
	if (stripTag) 
		return xmlText.substring( firstGT + 1, closeTag );
	else
		return xmlText.substring( firstGT, xmlText.indexOf( ">", closeTag ) );
};

if (d3.env.HAS_ACTIVEX && typeof( XSLTProcessor ) == "undefined")
{

	DEBUG$( "defining XSLTProcessor" );
	XSLTProcessor = function( )
	{
		this._xslt = null;
		this._params = new Array();
		this._proc = null;
	};
	XSLTProcessor.prototype.getTemplate = function( )
	{
		return this._xslt;
	}
	XSLTProcessor.prototype.setTemplate = function( template )
	{
		this.reset();
		this._xslt = template;
	}
	XSLTProcessor.prototype.importStylesheet = function( stylesheet )
	{
		this._xslt = d3._getVersionedActiveX( [ "MSXML2.XSLTemplate.5.0", "MSXML2.XSLTemplate.4.0", "MSXML2.XSLTemplate.3.0" ] );
		this._xslt.stylesheet = stylesheet;
		this._proc = null;
	};
	XSLTProcessor.prototype._getProc = function( )
	{
		if (!this._xslt) return null;
		if (!this._proc) this._proc = this._xslt.createProcessor();
		return this._proc;
	}
	XSLTProcessor.prototype.clearParameters = function( namespaceUri )
	{
		var xp = this._getProc();
		if (!xp) return;
		for (var k = 0; k < this._params.length; k++)
			xp.addParameter( this._params[k].name, null, d3.ifNot( namespaceUri, "" ) );
		this._params = new Array();
	};
	XSLTProcessor.prototype.getParameter = function( namespaceUri, localName )
	{
		var xp = this._getProc();
		if (!xp) return;
		for (var k = 0; k < this._params.length; k++)
			if (this._params[k].name == localName) return this._params[k].value;
		return null;
	};
	XSLTProcessor.prototype.setParameter = function( namespaceUri, localName, value )
	{
		var xp = this._getProc();
		if (!xp) return;
		for (var k = 0; k < this._params.length; k++)
			if (this._params[k].name == localName) break;
		
		this._params[k] = new d3.NameValue( localName, value );
		xp.addParameter( localName, value, d3.ifNot( namespaceUri, "" ) );
	};
	XSLTProcessor.prototype.reset = function( )
	{
		this._xslt = null;
		this._proc = null;
		this._params = new Array();
	}
	XSLTProcessor.prototype.removeParameter = function( namespaceUri, localName )
	{
		var xp = this._getProc();
		if (!xp) return;
		for (var k = 0; k < this._params.length; k++)
			if (this._params[k].name == localName)
			{
				this._params.splice( k, 1 );
				break;
			}
		xp.addParameter( localName, null, d3.ifNot( namespaceUri, "" ) );
	};
	XSLTProcessor.prototype.transformToDocument = function( doc, params, output )
	{
		var xp = this._getProc();
		if (!xp) return;
		if (params)
		{
			var a;
			if (typeof(params) == "array") a = params; else a = [params];
			for (var k = 0; k < a.length; k++)
				xp.addParameter( a[k].name, a[k].value );
		}
		xp.input = doc;
		if (typeof(output) != "undefined") xp.output = output;
		xp.transform( );
		if (typeof(output) != "undefined") return xp.output;
		
		var s = xp.output;
		if (s.isMarkup()) return new DOMDocument(s); 
		return s;
	};
	XSLTProcessor.prototype.transformToFragment = function( doc, owner, params )
	{
		var xp = this._getProc();
		if (!xp) return;
		var x = owner.createDocumentFragment();
		return this.transformToDocument( doc, params, x );
	};

}
	
LOG$( "d3.xml complete" );