/******************************************************************************
*  Copyright (c) 2006, Copernic
*******************************************************************************
*******************************************************************************
*  Summary:
*    XML Library
*
*  Description:
*    Library of functions to manipulate XML documents.
*
*  Author:
*    Louis-Philippe Lauzier
*
*  Depends on:
*   types.js
******************************************************************************/

function CopernicXMLTransform(p_OnTransformReady) {
    if (document.implementation && document.implementation.createDocument) { 
        this.XMLTransformObject = new MozillaXMLTransform(p_OnTransformReady,this);
    } else {
        this.XMLTransformObject = new IExplorerXMLTransform(p_OnTransformReady,this);
    }
}

CopernicXMLTransform.prototype.LoadAll = function(p_XMLurl, p_XSLurl) {
    this.LoadXML(p_XMLurl);
    this.LoadXSL(p_XSLurl);
}

CopernicXMLTransform.prototype.LoadXML = function(p_XMLurl) {
    this.XMLTransformObject.LoadXML(p_XMLurl);
}

CopernicXMLTransform.prototype.LoadXSL = function(p_XSLurl) {
    this.XMLTransformObject.LoadXSL(p_XSLurl);
}

CopernicXMLTransform.prototype.CacheAll = function(p_XMLdom, p_XSLdom) {
    this.CacheXML(p_XMLdom);
    this.CacheXSL(p_XSLdom);
}

CopernicXMLTransform.prototype.CacheXML = function(p_XMLdom) {
    this.XMLTransformObject.CacheXML(p_XMLdom);
}

CopernicXMLTransform.prototype.CacheXSL = function(p_XSLdom) {
    this.XMLTransformObject.CacheXSL(p_XSLdom);
}

CopernicXMLTransform.prototype.AddParameter = function(p_ParamName, p_ParamValue) {
    this.XMLTransformObject.AddParameter(p_ParamName, p_ParamValue);
}

CopernicXMLTransform.prototype.ClearParameters = function() {
    this.XMLTransformObject.ClearParameters();
}

CopernicXMLTransform.prototype.Transform = function(p_OnTransformDone) {
	this.OnTransformDone = p_OnTransformDone;
	var done = this.XMLTransformObject.InternalTransform();
	if (done && this.OnTransformDone) this.OnTransformDone(this);
}

CopernicXMLTransform.prototype.AppendTransformedDocumentToElement = function(p_Element) {
	this.XMLTransformObject.AppendTransformedDocument(p_Element);
}

CopernicXMLTransform.prototype.PasteTransformedDocumentToElement = function(p_Element) {
	this.XMLTransformObject.PasteTransformedDocument(p_Element);
}

CopernicXMLTransform.prototype.GetTransformedDocument = function() {
	return this.XMLTransformObject.transformedDocument;
}

CopernicXMLTransform.prototype.GetXMLDocument = function() {
	return this.XMLTransformObject.XMLDocument;
}

CopernicXMLTransform.prototype.GetXSLDocument = function() {
	return this.XMLTransformObject.XSLDocument;
}


// Internet Explorer XML Transform object
function IExplorerXMLTransform(p_OnTransformReady,p_Base) {
    // Add initialization  code here
    this.XMLTemplate = new ActiveXObject("MSXML2.XSLTemplate");
    this.XMLDocument = new ActiveXObject("MSXML2.DOMDocument");
    this.XSLDocument = null // will be created each time we need to load an XSL document;
    this.transformedDocument = null;
    this.baseObject = p_Base;
    
    this.XSLTLoaded = false;
    this.XMLLoaded = false;
    this.OnTransformReady = p_OnTransformReady;
    this.ParameterName = new Array();
    this.ParameterValue = new Array();
}

IExplorerXMLTransform.prototype.LoadXML = function(p_XMLurl) {
    var obj = this;
    this.XMLLoaded = false;

    // Start the XML Download
    this.XMLDocument.onreadystatechange = function() { obj.OnLoadXML(); };
    this.XMLDocument.load(p_XMLurl);
}

IExplorerXMLTransform.prototype.LoadXSL = function(p_XSLurl) {
    var obj = this;
    this.XSLTLoaded = false;

    // Start the XSL Download
    // We need to recreate the object every time, because its read only
    this.XSLDocument = new ActiveXObject("MSXML2.FreeThreadedDOMDocument");
    this.XSLDocument.onreadystatechange = function() { obj.OnLoadXSLT(); };
    this.XSLDocument.load(p_XSLurl);
}

IExplorerXMLTransform.prototype.CacheXML = function(p_XMLdom) {
    this.XMLLoaded = false;

    // Load the XML Document
    if (p_XMLdom!=null) {
        this.XMLDocument = p_XMLdom;
        this.XMLLoaded = true;
        this.OnAfterDownload();
    }
}

IExplorerXMLTransform.prototype.CacheXSL = function(p_XSLdom) {
    this.XSLTLoaded = false;

    // Load the XML Document
    if (p_XSLdom!=null) {
        // We need to recreate the object every time, because its read only
        this.XSLDocument = new ActiveXObject("MSXML2.FreeThreadedDOMDocument");
        // Note: Can't copy directly DOM object because XSL must stay "free threaded"
        this.XSLDocument.loadXML(p_XSLdom.xml);
        this.XSLTLoaded = true;
        this.OnAfterDownload();
    }
}

IExplorerXMLTransform.prototype.OnLoadXML = function() {
    if (this.XMLDocument.readyState == 4) {
        this.XMLLoaded = true;
        this.OnAfterDownload();
    }
    return this;
}

IExplorerXMLTransform.prototype.OnLoadXSLT = function() {
    if (this.XSLDocument.readyState == 4) {
        this.XSLTLoaded = true;
        this.OnAfterDownload();
    }
    return this;
}

IExplorerXMLTransform.prototype.OnAfterDownload = function() {
    if (this.XSLTLoaded && this.XMLLoaded && this.OnTransformReady) {
        this.OnTransformReady(this.baseObject);
    }
}

IExplorerXMLTransform.prototype.InternalTransform = function () {
    var success = false;

    if (this.XSLTLoaded && this.XMLLoaded) {
        try {
            this.XMLTemplate.stylesheet = this.XSLDocument;
            this.XSLProcessor = this.XMLTemplate.createProcessor();
            for (i=0; i < this.ParameterName.length; i++) {
                this.XSLProcessor.addParameter(this.ParameterName[i], this.ParameterValue[i]);
            }
            
            this.XSLProcessor.input = this.XMLDocument;
            this.XSLProcessor.transform();
            this.transformedDocument = this.XSLProcessor.output;
            success = true;
        } catch(e) {
            success = false;
        }
    }
    return success;
}

IExplorerXMLTransform.prototype.AppendTransformedDocument = function(p_Element) {
    p_Element.innerHTML += this.transformedDocument;
}

IExplorerXMLTransform.prototype.PasteTransformedDocument = function(p_Element) {
    p_Element.innerHTML = this.transformedDocument;
}

IExplorerXMLTransform.prototype.AddParameter = function(p_ParamName, p_ParamValue) {
    this.ParameterName[this.ParameterName.length] = p_ParamName;
    this.ParameterValue[this.ParameterValue.length] = p_ParamValue;
}

IExplorerXMLTransform.prototype.ClearParameters = function() {
    this.ParameterName = new Array();
    this.ParameterValue = new Array();
}


// Mozilla XML Transform object
function MozillaXMLTransform(p_OnTransformReady,p_Base) {
    // Add initialization  code here

    this.XSLDocument = null; // will be created each time we need to load an XSL document;
    this.XMLDocument = null; // will be created each time we need to load an XSL document; 
    this.transformedDocument = null;
    this.baseObject = p_Base;

    this.XSLTLoaded = false;
    this.XMLLoaded = false;
    this.OnTransformReady = p_OnTransformReady;
    this.ParameterName = new Array();
    this.ParameterValue = new Array();
}

MozillaXMLTransform.prototype.LoadXML = function(p_XMLurl) {
    var obj = this;
    this.XMLLoaded = false;
    
    // Start the XML Download
    this.XMLDocument = new XMLHttpRequest();
    this.XMLDocument.onreadystatechange = function() { obj.OnLoadXML(); };
    this.XMLDocument.open("GET", p_XMLurl);
    this.XMLDocument.send(null);    
}

MozillaXMLTransform.prototype.LoadXSL = function(p_XSLurl) {
    var obj = this;
    this.XSLTLoaded = false;

    // Start the XSL Download
    this.XSLDocument = new XMLHttpRequest();
    this.XSLDocument.onreadystatechange = function() { obj.OnLoadXSLT(); };
    this.XSLDocument.open("GET", p_XSLurl);
    this.XSLDocument.send(null);
}

MozillaXMLTransform.prototype.CacheXML = function(p_XMLdom) {
    this.XMLLoaded = false;

    // Load the XML Document
    if (p_XMLdom!=null) {
        this.XMLDocument = p_XMLdom;
        this.XMLLoaded = true;
        this.OnAfterDownload();
    }
}

MozillaXMLTransform.prototype.CacheXSL = function(p_XSLdom) {
    this.XSLTLoaded = false;

    // Load the XSL Document
    if (p_XSLdom!=null) {
        this.XSLDocument = p_XSLdom;
        this.XSLTLoaded = true;
        this.OnAfterDownload();
    }
}

MozillaXMLTransform.prototype.OnLoadXML = function() {
    if (this.XMLDocument.readyState == 4)
    {
      this.XMLLoaded = true;
      this.OnAfterDownload();
    }
    return this;
}

MozillaXMLTransform.prototype.OnLoadXSLT = function() {
    if (this.XSLDocument.readyState == 4)
    {
      this.XSLTLoaded = true;
      this.OnAfterDownload();
    }
    return this;
}

MozillaXMLTransform.prototype.OnAfterDownload = function() {
    if (this.XSLTLoaded && this.XMLLoaded && this.OnTransformReady) {
        this.OnTransformReady(this.baseObject);
    }
}

MozillaXMLTransform.prototype.InternalTransform = function () {
    var success = false;
    var resultDoc;
    if (this.XSLTLoaded && this.XMLLoaded) {

    try {
            this.XSLProcessor = new XSLTProcessor();
            for (i=0; i < this.ParameterName.length; i++) {
                this.XSLProcessor.setParameter(null, this.ParameterName[i], this.ParameterValue[i]);
            }

            if (typeof this.XSLProcessor.transformDocument == 'function') {
                // obsolete Mozilla interface
                resultDoc = document.implementation.createDocument("", "", null);
                this.XSLProcessor.transformDocument(this.XMLDocument.responseXML, this.XSLDocument.responseXML, resultDoc, null);
                this.transformedDocument = new XMLSerializer().serializeToString(resultDoc);
            }
            else {
                this.XSLProcessor.importStylesheet(this.XSLDocument.responseXML);
                resultDoc = this.XSLProcessor.transformToFragment(this.XMLDocument.responseXML, document);
                this.transformedDocument = resultDoc;
            }
            success = true;
        } catch(e) {
            success = false;
        }
    }
    return success;
}

MozillaXMLTransform.prototype.AppendTransformedDocument = function(p_Element) {
  if (typeof this.XSLProcessor.transformDocument == 'function') {
    p_Element.innerHTML = this.transformedDocument;
  }
  else {
    p_Element.innerHTML = '';
    p_Element.appendChild(this.transformedDocument);
  }
}

MozillaXMLTransform.prototype.PasteTransformedDocument = function(p_Element) {
    while (p_Element.childNodes.length>0) {
        p_Element.removeChild(p_Element.firstChild);
    }
    p_Element.appendChild(this.transformedDocument);
}

MozillaXMLTransform.prototype.DecodeTags = function(p_transformedDocument) {
    var decodableTags = p_transformedDocument.getElementsByName('Decodable');

    var DecodableText;
    for(var i = decodableTags.length - 1; i >= 0; i--) { 
        DecodableText = decodableTags[i].textContent;
        if(DecodableText==undefined || (DecodableText.indexOf('&')==-1 && DecodableText.indexOf('<')==-1) ) {
            // the null or markupless element needs no reworking
        } else {
            decodableTags[i].innerHTML = DecodableText; // that's the magic
        }
    }
}

MozillaXMLTransform.prototype.AddParameter = function(p_ParamName, p_ParamValue) {
    this.ParameterName[this.ParameterName.length] = p_ParamName;
    this.ParameterValue[this.ParameterValue.length] = p_ParamValue;
}

MozillaXMLTransform.prototype.ClearParameters = function() {
    this.ParameterName = new Array();
    this.ParameterValue = new Array();
}


// Static functions
var CopernicXML = {}; // Namespace

CopernicXML.CreateDocument = function(p_NamespaceURI, p_RootTagName, p_OnLoad) 
{
    var objDOM = null;
    
    //determine if this is a standards-compliant browser like Mozilla
    if (document.implementation && document.implementation.createDocument) {
    
        objDOM = document.implementation.createDocument(p_NamespaceURI, p_RootTagName, null);
        
        // register a handler for the OnLoad event
        if (p_OnLoad!=null) objDOM.onload = function() { p_OnLoad(objDOM); };
        
    } else { // Internet Explorer
    
        objDOM = new ActiveXObject("MSXML2.DOMDocument");

        //If there is a root tag name, we need to preload the DOM
        if (p_RootTagName) {
       
            //If there is both a namespace and root tag name, then
            //create an artifical namespace reference and load the XML.  
            if (p_NamespaceURI) {
                objDOM.loadXML("<a0:" + p_RootTagName + " xmlns:a0=\"" + p_NamespaceURI + "\" />");
            } else {
                objDOM.loadXML("<" + p_RootTagName + "/>");        
            }
        }

        // register a handler for the OnLoad event
        if (p_OnLoad!=null) objDOM.onreadystatechange = function() { if (objDOM.readyState==4) p_OnLoad(objDOM); };
    }
    
    //return the object
    return objDOM;
}

CopernicXML.GetXML = function(p_Document)
{
    var strXML = "";
    
    if (p_Document!=null && p_Document.hasChildNodes()==true) {
    
        if (window.XMLSerializer) { // Mozilla
            // create a new XMLSerializer
            var Serializer = new XMLSerializer();
            
            // get the XML string
            strXML = Serializer.serializeToString(p_Document);
        } else { // Internet Explorer
            // read the built-in xml property
            strXML = p_Document.xml;
        }
    }
    
    return strXML;
}

CopernicXML.SetXML = function(p_Document, p_XMLString)
{
    var success = true;
    
    if (p_Document!=null) {
    
        if (window.DOMParser) { // Mozilla
            
            // create a new DOMParser
            var Parser = new DOMParser();
            
            // create a temporary document from XML string
            var tempDoc = Parser.parseFromString(p_XMLString, "text/xml");
            
            // make sure to remove all nodes from the document
		    while (p_Document.hasChildNodes()) {
			    p_Document.removeChild(p_Document.lastChild);
			}
                
            // validate if a parsing error has occured
            if ((tempDoc.documentElement.tagName == "parserError") ||
                (tempDoc.documentElement.namespaceURI == "http://www.mozilla.org/newlayout/xml/parsererror.xml")) {
                // "reset" the temporary document
                tempDoc = document.implementation.createDocument("", "", null);
                success = false;
            }
            
            // add the nodes from the temporary document
            for (var i=0; i<tempDoc.childNodes.length; i++) {
                
                //import the node
                var importedNode = p_Document.importNode(tempDoc.childNodes[i], true);
                
                //append the imported node to the document
                p_Document.appendChild(importedNode);
            }
        } else { // Internet Explorer
            
            // load the string directly
            success = p_Document.loadXML(p_XMLString);
        }
    }
    
    return success;
}

// ATTENTION: This function works only when there is no namespace prefixes used
// in the XML document. However, a default namespace can be specified.
CopernicXML.SelectSingleNode = function(p_Document, p_XPathExpression)
{
    var node = null;

    if (p_Document.evaluate) { // Mozilla
        var resolver = function(p_Prefix) {
            if (p_Prefix == "default") {
                return p_Document.documentElement.namespaceURI;
            }
        }
        p_XPathExpression = p_XPathExpression.replace(/\//g,"/default:");
        var evaluator = p_Document.evaluate(p_XPathExpression,p_Document,resolver,9,null);
        
        if (evaluator != null) {
            node = evaluator.singleNodeValue;
        }
    } else { // Internet Explorer
        node = p_Document.selectSingleNode(p_XPathExpression);                
    }
    
    return node;
}

// ATTENTION: This function works only when there is no namespace prefixes used
// in the XML document. However, a default namespace can be specified.
CopernicXML.SelectNodes = function(p_Document, p_XPathExpression)
{
    var nodes = new Array();

    if (p_Document.evaluate) { // Mozilla
        var resolver = function(p_Prefix) {
            if (p_Prefix == "default") {
                return p_Document.documentElement.namespaceURI;
            }
        }
        p_XPathExpression = p_XPathExpression.replace(/\//g,"/default:");
        var evaluator = p_Document.evaluate(p_XPathExpression,p_Document,resolver,5,null);
        
        if (evaluator != null) {
            var n = null;
            while (n = evaluator.iterateNext()) {
                nodes.push(n);
            }
        }
    } else { // Internet Explorer
        nodes = p_Document.selectNodes(p_XPathExpression);                
    }
    
    return nodes;
}