//XMLRPC Response
function XMLRPCResponse( hParamNode )
{
	var hReturnValueNode = null
	hReturnValueNode = hParamNode.childNodes[ 0 ]
	while( hReturnValueNode.nodeType != document.ELEMENT_NODE )
	{
		hReturnValueNode = hReturnValueNode.nextSibling
	}
	this.hResponse = this.xmlToObject( hReturnValueNode )
}

XMLRPCResponse.prototype.xmlToObject = function( hParamNode )
{
	var hRetVal
	var hValue
	switch( hParamNode.nodeName )
	{
		case 'int'		:
		case 'i4'		:
		case 'double'	:	hValue = hParamNode.firstChild.nodeValue
							hRetVal = ( hValue ) ? new Number( hValue ) : 0
							delete hValue
							break
		case 'string'	:	hValue = getNodeText( hParamNode )
							hRetVal = ( hValue ) ? new String( hValue ) : ''
							delete hValue
							break
		case 'boolean'	:	hValue = getNodeText( hParamNode )
							hRetVal = isNaN( parseInt( hValue ) ) ? Boolean( hValue == 'true' ) : Boolean( parseInt( hValue ) )
							delete hValue
							break
		case "dateTime.iso8601":
							hRetVal = ISO8601ToDate( getNodeText( hParamNode ) )
							break
		case 'array'	:	hRetVal = new Array()
							var nVc = 0
							var hData = getNodeChild( hParamNode, 0 )
							while( hValue = getNodeChild( hData, nVc ) )
							{
								hRetVal[ nVc++ ] = this.xmlToObject( hValue )
								delete hValue
							}
							delete hData
							break
		case 'struct'	:	hRetVal = new Object()
							var nVc = 0
							while( hValue = getNodeChild( hParamNode, nVc++ ) )
							{
								hRetVal[ getNodeChild( hValue, 0 ).firstChild.nodeValue ] = this.xmlToObject( getNodeChild( hValue, 1 ) )
								delete hValue
							}
							break
		case 'value'	:	hRetVal = this.xmlToObject( getNodeChild( hParamNode, 0 ) )
							break
	}
	return hRetVal
}

//get the n-th child of a node with nodeType 1 (node element)
function getNodeChild( hNode, nChild )
{
	if( hNode != null )
	{
		var nNc = 0
		var nC	= 0
		var hNodeChildren = hNode.childNodes
		while( nC < hNodeChildren.length )
		{
			hCNode = hNodeChildren.item( nC++ )
			if( hCNode.nodeType  == 1 )
			{
				if( nNc == nChild )
				{
					return hCNode
				}
				nNc++
			}
		}
	}
	return false
}

//XMLRPC Request
function XMLRPCMessage( sMethodName )
{
	this.nID = ++XMLRPCMessage.ID
	this.sName = 'XMLRPCMessageID:'+this.nID
	this.sMethod = (typeof sMethodName == 'undefined') ? "" : sMethodName
	this.aParameters = new Array()
	this.hMessage = null
	this.bReady = false
	this.hServerRequest = XmlHttp.create()
	this.onResponse = null
	this.onFail = null
	return this
}
XMLRPCMessage.ID = 0
XMLRPCMessage.nTimeout = 10000
XMLRPCMessage.bDebug = false


XMLRPCMessageError = function( sDescription )
{
	this.sDescription = sDescription
}

XMLRPCMessage.prototype.createContext = function( sMethod )
{
	var hNewContext = this
	return function() { hNewContext[ sMethod ].apply( hNewContext, arguments ) }
} 

XMLRPCMessage.prototype.onReceiveResponse = function()
{
	if( this.hServerRequest.readyState == 4 )
	{
  		if( this.nReceiveTimeout )
  		{
			window.clearTimeout( this.nReceiveTimeout )
		}
		if( this.onResponse )
		{
			if( XMLRPCMessage.bDebug && debug ) 
			{
				debug( this.hServerRequest.responseText )
			}
		    if( this.hServerRequest.responseXML )
		    {
				var hParamNode = this.hServerRequest.responseXML.documentElement.getElementsByTagName( 'value' )[ 0 ]
				var hResponse = new XMLRPCResponse( hParamNode )
				if( hResponse.hResponse[ 'faultCode' ] )
				{
					if( this.onFail )
					{
					    this.onFail()
					}
				}
				else
				{
					this.onResponse( hResponse )
				}
			}
			else
			{
				this.onResponse( null )
			}
		}
	} 
}

XMLRPCMessage.prototype.send = function( sURL )
{
	if( !this.bReady )
	{
		this.buildXml()
	}
	this.hServerRequest.open( 'POST', sURL, true )
	this.hServerRequest.onreadystatechange = this.createContext( 'onReceiveResponse' )
	this.hServerRequest.send( this.hMessage )
	var hMessage = this
	this.nReceiveTimeout = window.setTimeout( function()
								{
									hMessage.hServerRequest.abort()
									if( hMessage.onFail )
									{
									    hMessage.onFail()
									}
								}, XMLRPCMessage.nTimeout )
}

XMLRPCMessage.prototype.setMethodName = function( sMethodName )
{
	this.sMethod = (typeof sMethodName == 'undefined') ? "" : sMethodName
}

XMLRPCMessage.prototype.addParameter = function( hParameter )
{
	this.aParameters[ this.aParameters.length ] = encodeURIComponent( hParameter )
}

XMLRPCMessage.getDataTypeOf = function( hParameter )
{
	sType = typeof hParameter
	sType = sType.toLowerCase()
	sRPCType = 'unknown'
	switch( sType )
	{
		case 'number' :	if( Math.round( hParameter ) == hParameter )
						{
							sRPCType = 'i4'
						}
						else
						{
							sRPCType = 'double'
						}
						break
		case 'object' :	if( hParameter.constructor == Date )
						{
							sRPCType = 'date'
						}
						else if( hParameter.constructor == Array )
						{
							sRPCType = 'array'
						}
						else
						{
							sRPCType = 'struct'
						}
						break
		default		  :	sRPCType = sType
						break
						
	}
	return sRPCType
}

XMLRPCMessage.prototype.buildXml = function()
{
	this.hMessage = XmlDocument.create()
	
	var sParamsXml = "<params>\n"
	for( nI = 0; nI < this.aParameters.length; nI++ )
	{
		sParamsXml += "<param>\n"
		sParamsXml += "<value>\n"
		sParamsXml += XMLRPCMessage.buildParameterXml( XMLRPCMessage.getDataTypeOf( this.aParameters[nI] ), this.aParameters[nI] )
		sParamsXml += "</value>\n"
		sParamsXml += "</param>\n"
	}
	sParamsXml += "</params>\n"
	
	var sXml = ""
	
	sXml += "<?xml version=\"1.0\"?>\n"
	sXml += "<methodCall>\n"
	sXml += "<methodName>" + this.sMethod + "</methodName>\n"
	sXml += sParamsXml
	sXml += "</methodCall>\n"
	
	if( XMLRPCMessage.bDebug && debug )
	{
		debug( sXml )
	}
	this.hMessage.loadXML( sXml )
	this.bReady = true
	return sXml
}

XMLRPCMessage.buildParameterXml = function( sNodeType, hNodeData )
{
	var sXml = ""
	switch( sNodeType )
	{
		case "date"	:	sXml = XMLRPCMessage.buildDateXml( hNodeData )
						break
		case "array" :	sXml = XMLRPCMessage.buildArrayXml( hNodeData )
						break
		case "struct" :	sXml = XMLRPCMessage.buildStructXml( hNodeData )
						break
		case "boolean" :
						sXml = XMLRPCMessage.buildBooleanXml( hNodeData )
						break
		default :		sXml = XMLRPCMessage.buildVarXml( sNodeType, hNodeData )
						break

	}
	return sXml
}

XMLRPCMessage.buildDateXml = function( hNodeData )
{
	var sXml = "<dateTime.iso8601>"
	sXml += dateToISO8601( hNodeData )
	sXml += "</dateTime.iso8601>"
	return sXml
}

XMLRPCMessage.buildArrayXml = function( hNodeData )
{
	var sXml = "<array><data>\n"
	for( nI = 0; nI < hNodeData.length; nI++ )
	{
		sXml += "<value>"
		sXml += XMLRPCMessage.buildParameterXml( XMLRPCMessage.getDataTypeOf( hNodeData[nI] ), hNodeData[nI] )
		sXml += "</value>\n"
	}
	sXml += "</data></array>\n"
	return sXml
}

XMLRPCMessage.buildStructXml = function( hNodeData )
{
	var sXml = "<struct>\n"
	for( hI in hNodeData )
	{
		sXml += "<member>\n"
		sXml += "<name>"
		sXml += hI
		sXml += "</name>\n"
		sXml += "<value>"
		sXml += XMLRPCMessage.buildParameterXml( XMLRPCMessage.getDataTypeOf( hNodeData[hI] ), hNodeData[hI] )
		sXml += "</value>"
		sXml += "</member>\n"
	}
	sXml += "</struct>\n"
	return sXml
}

XMLRPCMessage.buildBooleanXml = function( hNodeData )
{
	var sXml = "<boolean>"
	sXml += (hNodeData == true? "1":"0")
	sXml += "</boolean>\n"
	return sXml
}

XMLRPCMessage.buildVarXml = function( sNodeType, hNodeData )
{
	var sXml = "<"+sNodeType+">"
	sXml += hNodeData
	sXml += "</"+sNodeType+">\n"
	return sXml
}

//misc functions
function dateToISO8601( hDate )
{
	var year = new String(date.getYear());
	var month = leadingZero(new String(date.getMonth()));
	var day = leadingZero(new String(date.getDate()));
	var time = leadingZero(new String(date.getHours())) + ":" + leadingZero(new String(date.getMinutes())) + ":" + leadingZero(new String(date.getSeconds()));

	var converted = year+month+day+"T"+time;
	return converted;
}
  
function leadingZero(n){
  if (n.length==1) n = "0" + n;
  return n;
}

function ISO8601ToDate( sDate )
{
	var hReg = /(\d{4})(\d{2})(\d{2})T(\d{2}):(\d{2}):(\d{2})/
	if( hReg.test( sDate ) )
	{
		return new Date( RegExp.$1, RegExp.$2, RegExp.$3, RegExp.$4, RegExp.$5, RegExp.$6 )
	}
	else
	{
		return new Date
	}
}
