/** XmlWrapper3.js
 *    3'rd implementation of iPOV's XML/XSLT Object Wrapper.
 *    Provides Objects that hide the difference between XML implementations in different browsers
 *    Provides 'static' initializers for creating these objects.
 *    Provides object cache based on file path/name to cut down on parse times. 
 */  
//
var xw_TEXT_NODE = 3;
var XW_DOCS = new Object();

var xmlObjectCache = new Object();

function xw_isEmpty(obj) {
    var objEmpty;
    if ((objEmpty == obj) || (null == obj)) {
        return true;
    } else {
        //possibly other checks (for instance for empty strings)
        return false;
    }
}

function xw_getXmlObject(url, forceXml, includes) {
    // For now this is just passing straight on; but we will add in-memory caching to this.
    // For some reason in IE (maybe just w/ XSLT) the caching of XML Objects seems to be SLOWER than reloading from url.
    return xw_newXmlObject(url, forceXml, includes);
    /*
    if (!xmlObjectCache[url]) {
        //alert('Loading new XML Object from url: ' + url + '\r\n' + xmlObjectCache[url]);
        xmlObjectCache[url] = xw_newXmlObject(url, forceXml, includes);
    }
    return xmlObjectCache[url]; */
}

function xw_newXmlObject(url, forceXml, includes) {
    if (xw_isEmpty(url)) {
        return null;
    }
    //alert('xw_newXmlObject; url = ' + url);
    var xmlObject = null;
    if (url.indexOf('xwdoc://') == 0) {
        var name = url.substring(8);
        //alert('xwdoc name = ' + name);
        xmlObject = XW_DOCS[name];
    } else {
        if (window.ActiveXObject) {
            xmlObject = new XwIEXmlDocument(url, forceXml);
        } else {
            xmlObject = new XwDomXmlDocument(url, forceXml);
        }
        if (!xw_isEmpty(xmlObject)) {
            if (includes) {
                xiProcessIncludes(xmlObject);
            }
        }
    }
    return xmlObject;
}

function xw_putXmlObject(xwdoc, name) {
    XW_DOCS[name] = xwdoc;
    return 'xwdoc://' + name;
}

function xw_newXmlRequestObject() {
    //
}

function xw_createEmptyDocument(rootNodeName) {
    var xdoc = null;
    if ((document.implementation) && (document.implementation.createDocument)) {
        xdoc = new XwDomXmlDocument();
        xdoc.document = document.implementation.createDocument('',rootNodeName,null);
    } else if (window.ActiveXObject) {
        xdoc = new XwIEXmlDocument();
        xdoc.document.loadXML('<' + rootNodeName + ' />');
    }
    return xdoc;
}

function xw_childNodesToArray(parentNode) {
    var children = new Array();
    if ((!xw_isEmpty(parentNode)) && parentNode.hasChildNodes() ) {
        for (var i = 0; i < parentNode.childNodes.length; i++) {
            children[i] = parentNode.childNodes.item(i);
        }
    }
    return children;
}

function xw_getTextNodes(parentNode) {
    var children = xw_childNodesToArray(parentNode);
    var textNodes = new Array();
    var node;
    while (children.length > 0) {
        node = children.shift();
        if (xw_TEXT_NODE == node.nodeType) {
            textNodes.push(node);
        } else {
            var txts = xw_childNodesToArray(node);
            children = children.concat( txts );
        }
    }
    return textNodes;
}

function xiProcessIncludes(xwdoc) {
    var nodes = xwdoc.document.getElementsByTagName('XInclude');
    var pathBase = xiGetPathBase(xwdoc.src);
    //alert('XwProcessIncludes: pathBase = ' + pathBase);
    var j;  // define here for efficency
    var node = null;
    var parentNode = null;
    var xiNodes = null;
    //
    //alert('XwProcessIncludes: nodes.length = ' + nodes.length);
    for (var i = 0; i < nodes.length; i++) {
        node = nodes.item(i);
        xiNodes = xiResolveInclude(node, pathBase)
        if (!xw_isEmpty(xiNodes)) {
            //alert('xiResolveInclude; returned : xiNodes.length = ' + xiNodes.length);
            parentNode = node.parentNode;
            var importedNodes = new Array();
            //alert('XInclude pointed to document with: ' + xiNodes.length + ' child nodes.');
            for (var j = 0; j < xiNodes.length; j++) {
                var childNode = xiNodes[j];
                //alert('Importing node: ' + childNode.nodeName);
                //nodeNameList.push(childNode.nodeName);
                importedNodes.push( xwdoc.importNode(childNode) );
            }
            for (var j = 0; j < importedNodes.length; j++) {
                try {
                    parentNode.insertBefore(importedNodes[j], node);
                } catch (x) {
                    alert('Unable to process XInclude.\r\n[error # ' + x.number + ']\r\n' + x.description);
                    //alert(x);
                }
            }
            parentNode.removeChild(node);
        }
    }
    //alert('__ IMPORT COMPLETE __: ');
}

function xiResolveInclude(node, pathBase) {
    var src = node.getAttribute('document');
    //alert('xiResolveInclude; document = ' + src);
    var xdoc = xw_getXmlObject(pathBase + src);
    if (!xw_isEmpty(xdoc) && !xw_isEmpty(xdoc.document)) {
        var ele = xdoc.document.documentElement;
        if ('DocumentControl' == ele.nodeName) {
            return new Array(ele);
        } else {
            var childNodes = ele.childNodes;
            var childArray = new Array();
            for (var i = 0; i < childNodes.length; i++) {
                childArray[i] = childNodes.item(i);
            }
            return childArray;
        }
    } else {
        return null;
    }
}

function xiGetPathBase(path) {
    return path.substring(0, path.lastIndexOf('/') + 1);
}


// ----- INTERNAL OBJECTS ------------
function XwIEXmlDocument(url, forceXml) {
    //base object for 
    this.src = url;
    this.xmlObject = new ActiveXObject('Msxml2.FreeThreadedDOMDocument');   //FreeThreaded - required for XSLT...
    this.xmlObject.async = false;
    this.xmlObject.validateOnParse = false;
    if (!xw_isEmpty(url)) {
        this.load(url, forceXml);
    }
    this.document = this.xmlObject;
}
XwIEXmlDocument.prototype.load = function (url, forceXml) {
    try {
        this.xmlObject.load(url);
    } catch (e) {
        alert('Error loading xml.\r\n[error #' + e.number + ']\r\n' + e.description);
    }
    if (this.xmlObject.parseError.errorCode != 0) {
        // THERE IS AN ERROR
        alert('XML Loading error: ' + this.xmlObject.parseError.reason);
        //this.xmlObject.parseError.reason = text
    }
};
/** Convert in-memory string into an XML Document. */
XwIEXmlDocument.prototype.loadXML = function (str) {
    this.document.loadXML(str);
    return this;
};
XwIEXmlDocument.prototype.importNode = function (node) {
    // IE Seems to be able to copy nodes from one document to another without problems...
    return node;        //this.document.cloneNode(node, true);
};
XwIEXmlDocument.prototype.toString = function () {
    if (!xw_isEmpty(this.document)) {
        return this.document.xml;
    } else {
        return "Empty XwIEXmlDocument.";
    }
};
XwIEXmlDocument.prototype.getAttributeValue = function (node, attrName, defaultValue) {
    if (node.attributes) {
        var attr = node.attributes.getNamedItem(attrName);
        if (attr) {
            return attr.nodeValue;
        }
    }
    return (defaultValue) ? defaultValue : null;
};

/** Implementation for Gecko (Firefox/Mozilla) engine, 
 *   and other browsers that have a XMLHttpRequest javascript object. */
function XwDomXmlDocument(url, forceXml) {
    this.src = url;
    this.xmlLoader = new XMLHttpRequest();
    if (!xw_isEmpty(url)) {
        this.document = this.load(url, forceXml);
    }
}
XwDomXmlDocument.prototype.unserialize = function (str) {
    //convert string to xml document object
    var xparser = new DOMParser();
    return xparser.parseFromString(str, 'text/xml');
};
XwDomXmlDocument.prototype.serialize = function (xdoc) {
    //convert xml document object to string
    if (xw_isEmpty(xdoc)) {
        return null;
    }
    
    var xser = new XMLSerializer();
    if (xdoc.document) {
        return xser.serializeToString(xdoc.document);
    } else {
        return xser.serializeToString(xdoc);
    }
};
XwDomXmlDocument.prototype.load = function (url, forceXml) {
    var xmlLoader = this.xmlLoader;
    xmlLoader.open("GET", url, false);
    if (forceXml) {
        xmlLoader.overrideMimeType('text/xml');
    }
    try {
        xmlLoader.send(null);
    } catch (e) {
        alert('Error loading XML.\r\n[error #' + e.number + ']\r\n' + e.description);
        //parent.frames['frame-view'].document.documentElement.innerHTML =  xmlLoader.responseXML;
    }
    //alert('xmlLoader.responseText = \r\n\r\n' + xmlLoader.responseText);
    if ((forceXml) && (xw_isEmpty(xmlLoader.responseXML))) {
        return this.unserialize(xmlLoader.responseText);
    } else {
        //alert('xmlLoader.responseXML : \r\n\r\n' + xmlLoader.responseXML);
        return xmlLoader.responseXML;
    }
};
XwDomXmlDocument.prototype.importNode = function (node) {
    return this.document.importNode(node, true);
};
XwDomXmlDocument.prototype.toString = function() {
    return this.serialize(this.document);
};
XwDomXmlDocument.prototype.getAttributeValue = function (node, attrName) {
    return null;
};
