/*
  CLASS LIST:

	* LIXmlApi
	* LIXmlDom
*/

/*############################################################
	Liquid Intelligence:
	 Xml Api Class
 ############################################################*/
/*==========================
 Constructor
 ==========================*/ 
function LIXmlApi(n_application)
{ 
	LIXmlApi.prototype.Application = n_application;	//Application we can call for helpers.
}


/*==========================
 NewDom
 ==========================*/ 
LIXmlApi.prototype.NewDom = function LIXmlApi_NewDom(n_xml)
{
	xd = new LIXmlDom(this.Application);
	xd.SetDom(n_xml);
	return xd;
}


/*==========================
 NewMSDom

 Creates a new DOM depending on the input object type.
 ==========================*/ 
LIXmlApi.prototype.NewMSDom = function LIXmlApi_NewMSDom(n_value)
{
	var NewMSDom = new ActiveXObject("Microsoft.XMLDOM");

	//STRING
	if(typeof(n_value) == "string")
	{
		if( n_value.search(/\.xsl$|\.xml$/ig) == -1)
		{
			NewMSDom.loadXML(n_value);
		}
		else 
		{
			NewMSDom.async = false;
			NewMSDom.load(n_value);
		}
	}
	//XMLDOM?
	else if(typeof(n_value) == "object")
	{
		NewMSDom.loadXML = n_value.xml;
	}
	//NO INPUT
	else if(typeof(n_value) == "undefined")
	{
	}

	return NewMSDom;
}


/*==========================
 NewMSRemoteDom
 ==========================*/ 
LIXmlApi.prototype.NewMSRemoteDom = function LIXmlApi_NewMSRemoteDom()
{
	return new ActiveXObject("Microsoft.XMLHTTP");
}


/*==========================
 RemoteXmlCall
 ==========================*/ 
LIXmlApi.prototype.RemoteXmlCall = function LIXmlApi_RemoteXmlCall(n_url, n_requestDom)
{
	var remoteDom = LIXmlApi.prototype.NewMSRemoteDom();
	
	remoteDom.open("POST", n_url, false);
	remoteDom.setRequestHeader("Content-Type", "text/xml; Charset='UTF-8';");
	remoteDom.setrequestheader("Pragma", "no-cache");
	remoteDom.setrequestheader("Cache-control", "no-cache");
	
	var request = n_requestDom.xml;
	remoteDom.send(request);

	return this.NewDom( remoteDom.responseText );
	
}



/*############################################################
	Liquid Intelligence:
	 Xml Dom Class
 ############################################################*/
 /**********************************
 Liquid Intelligence Xml Dom
 **********************************/
/*==========================
 Constructor
 ==========================*/ 
function LIXmlDom(n_application)
{ 

	LIXmlDom.prototype.Application = n_application;	// Application we can call for helpers.
	LIXmlDom.prototype.m_selectedNode;	// Last node we worked with
	LIXmlDom.prototype.m_lastAppended;	// Last node we appended
	
	LIXmlDom.prototype.LastErrorMessage;
	LIXmlDom.prototype.LastErrorDetails;
	
	LIXmlDom.prototype.RawXml = "";	// The unparsed XML (only set if unparseable)
	LIXmlDom.prototype.Dom	= this.Application.Xml.NewMSDom(); // The XML we are wrapping
	
	LIXmlDom.prototype.USER_ERROR = 0x80040200; // Base "user" automation error 
 	Error.prototype.source = "Pandell Liquid Intelligence"; // Fills in the automation "source"
	
}


/*==================================
  Get Node Value 
  
  Gets value from element or attribute
  at given Xpath. If doesn't exist, returns
  the default value.

 ===================================*/
function LIXmlDom_GetNodeValue(n_xpath, n_defaultValue)
{
	var node = this.Dom.selectSingleNode(n_xpath);

	return ( node == null ) ? n_defaultValue : node.text;
}
LIXmlDom.prototype.GetNodeValue = LIXmlDom_GetNodeValue;

/*==================================
  Get Node Value 
  
  Gets value from element or attribute
  at given Xpath. If doesn't exist, returns
  the default value.

 ===================================*/
function LIXmlDom_SetNodeValue(xpath, value)
{
	var node = this.Dom.selectSingleNode(xpath);
	node.text = value;
}
LIXmlDom.prototype.SetNodeValue = LIXmlDom_SetNodeValue;

/*==================================
  Check Node Exists 
  
  Checks to see if a node at a
  given xpath exists  

 ===================================*/
function LIXmlDom_CheckNodeExists(n_xpath)
{
	return ( this.Dom.selectSingleNode(n_xpath) != null );
}
LIXmlDom.prototype.CheckNodeExists = LIXmlDom_CheckNodeExists;

/*================================
 Is Valid HTML Response (?)
 
 Checks to see if Xml contains
 a valid LI response.
 
 ================================*/
function LIXmlDom_IsValidHtmlResponse()
{
	if (this.Dom == null)
	{
		this.LastErrorMessage = "No Dom object present.";
		this.LastErrorDetails = "";
		return false;
	}

	if (this.Dom.parseError != 0)
	{
		return true;
	}

	//Error XML will look like this
	//<exception><message>Descriptive Text</message><details>lots of details, including stack trace</details></exception>
	if ( this.CheckNodeExists("//exception") )
	{
		this.LastErrorMessage = this.GetNodeValue("//message", "No message returned");
		this.LastErrorDetails = this.GetNodeValue("//details", "No details returned");
		return false;
	}

	return true;
}
LIXmlDom.prototype.IsValidHtmlResponse = LIXmlDom_IsValidHtmlResponse;



/*================================
 Is Valid Response (?)
 
 Checks to see if Xml contains
 a valid LI response.
 
 ================================*/
function LIXmlDom_IsValidResponse()
{
	if (this.Dom == null)
	{
		this.LastErrorMessage = "No Dom object present.";
		this.LastErrorDetails = "";
		return false;
	}

	if (this.Dom.parseError != 0)
	{
		this.LastErrorMessage = "Internal Dom Error.";
		this.LastErrorDetails = "";
		return false;
	}

	//Error XML will look like this
	//<exception><message>Descriptive Text</message><details>lots of details, including stack trace</details></exception>
	if ( this.CheckNodeExists("//exception") )
	{
		this.LastErrorMessage = this.GetNodeValue("//message", "No message returned");
		this.LastErrorDetails = this.GetNodeValue("//details", "No details returned");
		return false;
	}

	return true;
}
LIXmlDom.prototype.IsValidResponse = LIXmlDom_IsValidResponse;

/*=======================
 Set Dom
 
 The String/MS Dom we are wrapping

 =======================*/
function LIXmlDom_SetDom(n_xml)
{
	inputXml = n_xml;

	//We assume object is a Dom,
	//Convert anything else into a dom
	if( typeof(inputXml) != "object" )
	{
		inputXml = this.Application.Xml.NewMSDom(inputXml)
	}
	
	if (0 != inputXml.parseError.errorCode && typeof(n_xml) == "string")
	{
		this.RawXml = n_xml;
	}
	else
	{
		this.RawXml = "";
	}
	
	if (0 != inputXml.parseError.errorCode)
	{
		this.LastErrorMessage = "Error Setting Dom";
		this.LastErrorDetails = inputXml.parseError.reason;
	}
	
	this.Dom = inputXml;
	this.m_selectedNode = null;
	this.m_lastAppended = null;

}
LIXmlDom.prototype.SetDom = LIXmlDom_SetDom;

/*=======================
 To String
 =======================*/
function LIXmlDom_ToString()
{
	return (null != this.Dom && 0 == this.Dom.parseError.errorCode) ? this.Dom.xml : this.RawXml;
}
LIXmlDom.prototype.ToString = LIXmlDom_ToString;


/*=======================
 GetErrorMessage()
 =======================*/
LIXmlDom.prototype.GetErrorMessage = function LIXmlDom_GetErrorMessage()
{
	iParseError = this.Dom.parseError;

	var reason = iParseError.reason;
	var line = iParseError.line;
	var srcText = iParseError.srcText;
	
	window.clipboardData.setData( "Text", reason + "\n" + line + "\n" + srcText)

	return this.LastErrorMessage;
}


/*=======================
 ShowErrorDialog()
 =======================*/
LIXmlDom.prototype.ShowErrorDialog = function LIXmlDom_ShowErrorDialog()
{
	var e = this.GetErrorMessage();
	this.Application.ShowInformation(e);
}


/*=======================
 Error To Html
 =======================*/
function LIXmlDom_ErrorToHtml()
{
	iParseError = this.Dom.parseError;

	var reason = iParseError.reason;
	var line = iParseError.line;
	var srcText = iParseError.srcText;
	
	var e = "<table cellpadding=4 border=1 bordercolor=black style='border-collapse: collapse; border: 2 solid black; font-family: arial; font-size: 10pt;'><tr><td colspan='2' bgcolor='#FFC0C0'><b>ERROR</b></td></tr>";
	e = e +	"<tr><td bgcolor='#E0E0E0'>Message</td><td>" + this.LastErrorMessage + "</td></tr>";
	e = e +	"<tr><td bgcolor='#E0E0E0'>Reason</td><td>" + reason + "</td></tr>";
	if (reason != this.LastErrorDetails){e = e + "<tr><td bgcolor='#E0E0E0'>Details</td><td>" + this.LastErrorDetails + "</td></tr>"; }
	e = e +	"<tr><td bgcolor='#E0E0E0'>Line</td><td>" + line + "</td></tr>";
	if (srcText != "") { e = e +	"<tr><td> bgcolor='#E0E0E0'Source Text</td><td>" + srcText + "</td></tr>" };
	e = e +	"<tr><td bgcolor='#E0E0E0'>Full Xml</td><td>" + this.RawXml.replace(/</g, "&lt;").replace(/>/g, "&gt;") + "</td></tr>";
		e = e + "</table>";
	
	return e;
	
}
LIXmlDom.prototype.ErrorToHtml = LIXmlDom_ErrorToHtml;

/*=======================
 Set Root Name
 =======================*/
function LIXmlDom_SetRootName(n_rootName)
{

	var n_rootName = n_rootName.substring(1, n_rootName.length - 1);

	this.Dom.loadXML("<" + n_rootName + ">" +
						 "</" + n_rootName + ">");

	this.SelectRoot();
}
LIXmlDom.prototype.SetRootName = LIXmlDom_SetRootName;

/*=======================
 Make Root Current Node
 =======================*/
LIXmlDom.prototype.SelectRoot = function LIXmlDom_SelectRoot()
{
	this.m_selectedNode = this.Dom.documentElement;
	this.m_lastAppended = this.Dom.documentElement;
}


/*=======================
 AppendNode
 =======================*/
function LIXmlDom_AppendNode(nodeToAdd, cloneFlag)
{
	if (this.m_selectedNode == null) { throw new Error( this.USER_ERROR + 1000, "No Node Selected." ); }  
	if (cloneFlag) { this.m_selectedNode.appendChild(nodeToAdd.cloneNode(true)); }
	else { this.m_selectedNode.appendChild(nodeToAdd); }
}
LIXmlDom.prototype.AppendNode = LIXmlDom_AppendNode;

/*=======================
 Append Element to Current Node
 n_strElementName:
   element_tagname
   <element_tagname>
   @attribute_name
 =======================*/
function LIXmlDom_Add(n_elementName, n_value, n_value2)
{
 
  if (this.m_selectedNode == null) { throw new Error( this.USER_ERROR + 1000, "No Node Selected." ); }  
  if (n_elementName == null || n_elementName == ""){ throw new Error( this.USER_ERROR + 1000, "Element Name Not Supplied." ); }

  var postIndentFlag = false;
  var postOutdentFlag = false;

//QUICK INDENT?
  if (n_elementName.substring(0,1) == "+")
  {
	n_elementName = n_elementName.substring(1, n_elementName.length);
	this.Indent();
  }

//POST INDENT?
  if (n_elementName.search(/\+$/ig) != -1) 
  { 
	n_elementName = n_elementName.substring(0, n_elementName.length -1);
	postIndentFlag = true; 
  }

//POST OUTDENT?
  if (n_elementName.search(/\-$/ig) != -1) 
  { 
	n_elementName = n_elementName.substring(0, n_elementName.length -1);
	postOutdentFlag = true; 
  }

//QUICK OUTDENT?
  if (n_elementName.substring(0,1) == "-")
  {
    n_elementName = n_elementName.substring(1, n_elementName.length);
	this.Outdent();
  }

//ATTRIBUTE?
  if (n_elementName.substring(0,1) == "@")
  {
     var attributeName = n_elementName.substring(1, n_elementName.length);
     this.m_lastAppended.setAttribute(attributeName, n_value);
     if(postIndentFlag) {this.Indent();}
     if(postOutdentFlag) {this.Outdent();}
     return null;
  }
  
//ELEMENT?
  if (n_elementName.substring(0,1) == "<")
  {
	var n_elementName = n_elementName.substring(1, n_elementName.length - 1);
	var element = this.Dom.createElement(n_elementName);
	this.m_lastAppended = this.m_selectedNode.appendChild(element);
	
	//Attribute on Element line?
	if(n_value != null && n_value.substring(0,1) == "@" && n_value2 != null)
	{
		var attributeName = n_value.substring(1, n_value.length);
		this.m_lastAppended.setAttribute(attributeName, n_value2);	
	}
	else
	{
		if(n_value != null) { this.m_lastAppended.text = n_value; }
	}
	
	if(postOutdentFlag) {this.Outdent();}
	if(postIndentFlag) {this.Indent();}
	return this.m_lastAppended;
  }

}
LIXmlDom.prototype.Add = LIXmlDom_Add;

/*=======================
  Indent
  =======================*/
function LIXmlDom_Indent()
{
	this.m_selectedNode = this.m_lastAppended;
	return this.m_selectedNode;
}	
LIXmlDom.prototype.Indent = LIXmlDom_Indent;

/*=======================
  Outdent
  =======================*/
function LIXmlDom_Outdent()
{
	this.m_selectedNode = this.m_selectedNode.parentNode;
	this.m_lastAppended = this.m_selectedNode;
	return this.m_selectedNode;
}
LIXmlDom.prototype.Outdent = LIXmlDom_Outdent;



//-----------------------------------------------
// Remove the specified node from this DOM.
//
// The node to be removed can be specified
// as xpath (when the argument is of type
// 'string') or the actual node object
// (when the argument is of type other than 'string').
//
// Note that after successfull removal the current
// node selection is reset by calling "SelectRoot"
// method of this object.
//-----------------------------------------------
LIXmlDom.prototype.RemoveNode = function LIXmlDom_RemoveNode(xpath)
{
	var node = xpath;
	if ("string" == typeof(xpath)) {
		node = this.Dom.selectSingleNode(xpath);
	}
	if (null == node) {
		throw "XPath node \"" + xpath + "\" was not found";
	}
	if (null == node.parentNode) {
		throw "XPath node \"" + xpath + "\" describes root element";
	}
	node.parentNode.removeChild(node);

	this.SelectRoot();
}
