// ========================================================================
//	XML.ObjTree -- XML source code from/to JavaScript object like E4X
// ========================================================================

if ( typeof(XML) == 'undefined' ) XML = function() {};

//	constructor

XML.ObjTree = function () {
	return this;
};

//	class variables

XML.ObjTree.VERSION = "0.24";

//	object prototype

XML.ObjTree.prototype.xmlDecl = '<?xml version="1.0" encoding="UTF-8" ?>\n';
XML.ObjTree.prototype.attr_prefix = '-';
XML.ObjTree.prototype.overrideMimeType = 'text/xml';

//	method: parseXML( xmlsource )

XML.ObjTree.prototype.parseXML = function ( xml ) {
	var root;
	if ( window.DOMParser ) {
		var xmldom = new DOMParser();
//		xmldom.async = false;			// DOMParser is always sync-mode
		var dom = xmldom.parseFromString( xml, "application/xml" );
		if ( ! dom ) return;
		root = dom.documentElement;
	} else if ( window.ActiveXObject ) {
		xmldom = new ActiveXObject('Microsoft.XMLDOM');
		xmldom.async = false;
		xmldom.loadXML( xml );
		root = xmldom.documentElement;
	}
	if ( ! root ) return;
	return this.parseDOM( root );
};

//	method: parseHTTP( url, options, callback )

XML.ObjTree.prototype.parseHTTP = function ( url, options, callback ) {
	var myopt = {};
	for( var key in options ) {
		myopt[key] = options[key];					// copy object
	}
	if ( ! myopt.method ) {
		if ( typeof(myopt.postBody) == "undefined" &&
			 typeof(myopt.postbody) == "undefined" &&
			 typeof(myopt.parameters) == "undefined" ) {
			myopt.method = "get";
		} else {
			myopt.method = "post";
		}
	}
	if ( callback ) {
		myopt.asynchronous = true;					// async-mode
		var __this = this;
		var __func = callback;
		var __save = myopt.onComplete;
		myopt.onComplete = function ( trans ) {
			var tree;
			if ( trans && trans.responseXML && trans.responseXML.documentElement ) {
				tree = __this.parseDOM( trans.responseXML.documentElement );
			} else if ( trans && trans.responseText ) {
				tree = __this.parseXML( trans.responseText );
			}
			__func( tree, trans );
			if ( __save ) __save( trans );
		};
	} else {
		myopt.asynchronous = false; 				// sync-mode
	}
	var trans;
	if ( typeof(HTTP) != "undefined" && HTTP.Request ) {
		myopt.uri = url;
		var req = new HTTP.Request( myopt );		// JSAN
		if ( req ) trans = req.transport;
	} else if ( typeof(Ajax) != "undefined" && Ajax.Request ) {
		var req = new Ajax.Request( url, myopt );	// ptorotype.js
		if ( req ) trans = req.transport;
	}
//	if ( trans && typeof(trans.overrideMimeType) != "undefined" ) {
//		trans.overrideMimeType( this.overrideMimeType );
//	}
	if ( callback ) return trans;
	if ( trans && trans.responseXML && trans.responseXML.documentElement ) {
		return this.parseDOM( trans.responseXML.documentElement );
	} else if ( trans && trans.responseText ) {
		return this.parseXML( trans.responseText );
	}
}

//	method: parseDOM( documentroot )

XML.ObjTree.prototype.parseDOM = function ( root ) {
	if ( ! root ) return;

	this.__force_array = {};
	if ( this.force_array ) {
		for( var i=0; i<this.force_array.length; i++ ) {
			this.__force_array[this.force_array[i]] = 1;
		}
	}

	var json = this.parseElement( root );	// parse root node
	if ( this.__force_array[root.nodeName] ) {
		json = [ json ];
	}
	if ( root.nodeType != 11 ) {			// DOCUMENT_FRAGMENT_NODE
		var tmp = {};
		tmp[root.nodeName] = json;			// root nodeName
		json = tmp;
	}
	return json;
};

//	method: parseElement( element )

XML.ObjTree.prototype.parseElement = function ( elem ) {
	//	COMMENT_NODE
	if ( elem.nodeType == 7 ) {
		return;
	}

	//	TEXT_NODE CDATA_SECTION_NODE
	if ( elem.nodeType == 3 || elem.nodeType == 4 ) {
		var bool = elem.nodeValue.match( /[^\x00-\x20]/ );
		if ( bool == null ) return; 	// ignore white spaces
		return elem.nodeValue;
	}

	var retval;
	var cnt = {};

	//	parse attributes
	if ( elem.attributes && elem.attributes.length ) {
		retval = {};
		for ( var i=0; i<elem.attributes.length; i++ ) {
			var key = elem.attributes[i].nodeName;
			if ( typeof(key) != "string" ) continue;
			var val = elem.attributes[i].nodeValue;
			if ( ! val ) continue;
			key = this.attr_prefix + key;
			if ( typeof(cnt[key]) == "undefined" ) cnt[key] = 0;
			cnt[key] ++;
			this.addNode( retval, key, cnt[key], val );
		}
	}

	//	parse child nodes (recursive)
	if ( elem.childNodes && elem.childNodes.length ) {
		var textonly = true;
		if ( retval ) textonly = false; 	   // some attributes exists
		for ( var i=0; i<elem.childNodes.length && textonly; i++ ) {
			var ntype = elem.childNodes[i].nodeType;
			if ( ntype == 3 || ntype == 4 ) continue;
			textonly = false;
		}
		if ( textonly ) {
			if ( ! retval ) retval = "";
			for ( var i=0; i<elem.childNodes.length; i++ ) {
				retval += elem.childNodes[i].nodeValue;
			}
		} else {
			if ( ! retval ) retval = {};
			for ( var i=0; i<elem.childNodes.length; i++ ) {
				var key = elem.childNodes[i].nodeName;
				if ( typeof(key) != "string" ) continue;
				var val = this.parseElement( elem.childNodes[i] );
				if ( ! val ) continue;
				if ( typeof(cnt[key]) == "undefined" ) cnt[key] = 0;
				cnt[key] ++;
				this.addNode( retval, key, cnt[key], val );
			}
		}
	}
	return retval;
};

//	method: addNode( hash, key, count, value )

XML.ObjTree.prototype.addNode = function ( hash, key, cnts, val ) {
	if ( this.__force_array[key] ) {
		if ( cnts == 1 ) hash[key] = [];
		hash[key][hash[key].length] = val;		// push
	} else if ( cnts == 1 ) {					// 1st sibling
		hash[key] = val;
	} else if ( cnts == 2 ) {					// 2nd sibling
		hash[key] = [ hash[key], val ];
	} else {									// 3rd sibling and more
		hash[key][hash[key].length] = val;
	}
};

//	method: writeXML( tree )

XML.ObjTree.prototype.writeXML = function ( tree ) {
	var xml = this.hash_to_xml( null, tree );
	return this.xmlDecl + xml;
};

//	method: hash_to_xml( tagName, tree )

XML.ObjTree.prototype.hash_to_xml = function ( name, tree ) {
	var elem = [];
	var attr = [];
	for( var key in tree ) {
		if ( ! tree.hasOwnProperty(key) ) continue;
		var val = tree[key];
		if ( key.charAt(0) != this.attr_prefix ) {
			if ( typeof(val) == "undefined" || val == null ) {
				elem[elem.length] = "<"+key+" />";
			} else if ( typeof(val) == "object" && val.constructor == Array ) {
				elem[elem.length] = this.array_to_xml( key, val );
			} else if ( typeof(val) == "object" ) {
				elem[elem.length] = this.hash_to_xml( key, val );
			} else {
				elem[elem.length] = this.scalar_to_xml( key, val );
			}
		} else {
			attr[attr.length] = " "+(key.substring(1))+'="'+(this.xml_escape( val ))+'"';
		}
	}
	var jattr = attr.join("");
	var jelem = elem.join("");
	if ( typeof(name) == "undefined" || name == null ) {
		// no tag
	} else if ( elem.length > 0 ) {
		if ( jelem.match( /\n/ )) {
			jelem = "<"+name+jattr+">\n"+jelem+"</"+name+">\n";
		} else {
			jelem = "<"+name+jattr+">"	+jelem+"</"+name+">\n";
		}
	} else {
		jelem = "<"+name+jattr+" />\n";
	}
	return jelem;
};

//	method: array_to_xml( tagName, array )

XML.ObjTree.prototype.array_to_xml = function ( name, array ) {
	var out = [];
	for( var i=0; i<array.length; i++ ) {
		var val = array[i];
		if ( typeof(val) == "undefined" || val == null ) {
			out[out.length] = "<"+name+" />";
		} else if ( typeof(val) == "object" && val.constructor == Array ) {
			out[out.length] = this.array_to_xml( name, val );
		} else if ( typeof(val) == "object" ) {
			out[out.length] = this.hash_to_xml( name, val );
		} else {
			out[out.length] = this.scalar_to_xml( name, val );
		}
	}
	return out.join("");
};

//	method: scalar_to_xml( tagName, text )

XML.ObjTree.prototype.scalar_to_xml = function ( name, text ) {
	if ( name == "#text" ) {
		return this.xml_escape(text);
	} else {
		return "<"+name+">"+this.xml_escape(text)+"</"+name+">\n";
	}
};

//	method: xml_escape( text )

XML.ObjTree.prototype.xml_escape = function ( text ) {
	return String(text).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
};

// Copyright (c) 2005-2006 Yusuke Kawasaki. All rights reserved.

