xml.js

Summary

Shared utilities for manipulating XML. Some parts owe a great deal to the Sarissa project: http://sarissa.sourceforge.net

Version: 0.8 $Id: overview-summary-xml.js.html,v 1.15 2008/02/20 18:47:09 jameso Exp $

Author: James A. Overton


/* ***** BEGIN LICENSE BLOCK *****
 * Licensed under Version: MPL 1.1/GPL 2.0/LGPL 2.1
 * Full Terms at http://mozile.mozdev.org/0.8/LICENSE
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is James A. Overton's code (james@overton.ca).
 *
 * The Initial Developer of the Original Code is James A. Overton.
 * Portions created by the Initial Developer are Copyright (C) 2005-2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *	James A. Overton <james@overton.ca>
 *
 * ***** END LICENSE BLOCK ***** */

/**
 * @fileoverview Shared utilities for manipulating XML.
 * Some parts owe a great deal to the Sarissa project: http://sarissa.sourceforge.net
 * @link http://mozile.mozdev.org 
 * @author James A. Overton <james@overton.ca>
 * @version 0.8
 * $Id: overview-summary-xml.js.html,v 1.15 2008/02/20 18:47:09 jameso Exp $
 */


mozile.provide("mozile.xml.*");

/**
 * A collection of XML functions.
 * @type Object
 */
mozile.xml = new Object();
// JSDoc hack
mozile.xml.prototype = new mozile.Module;


/**
 * A comprehensive list of XML namespaces.
 * Copied from Dojo 2.2.
 */
mozile.xml.ns = {
	AdobeExtensions : "http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/",
	atom : "http://www.w3.org/2005/Atom",
	cml : "http://www.xml-cml.org",
	dc : "http://purl.org/dc/elements/1.1/",
	dcq : "http://purl.org/dc/qualifiers/1.0",
	dt : "http://www.w3.org/2001/XMLSchema-datatypes",
	fo : "http://www.w3.org/1999/XSL/Format",
	mes : "http://mozile.mozdev.org/ns/mes/1.0", // Mozile's editing scheme
	mml : "http://www.w3.org/1998/Math/MathML",
	rdf : "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
	rdfs : "http://www.w3.org/2000/01/rdf-schema#",
	rng : "http://relaxng.org/ns/structure/1.0",
	saxon : "http://icl.com/saxon",
	"soap-env" : "http://schemas.xmlsoap.org/soap/envelope/",
	smil : "http://www.w3.org/2001/SMIL20/",
	svg : "http://www.w3.org/2000/svg",
	wsdl : "http://schemas.xmlsoap.org/wsdl/",
	xalan : "http://xml.apache.org/xslt",
	xbl : "http://www.mozilla.org/xbl",
	xforms : "http://www.w3.org/2002/01/xforms",
	xhtml : "http://www.w3.org/1999/xhtml",
	xi : "http://www.w3.org/2001/XInclude",
	xlink : "http://www.w3.org/1999/xlink",
	xsd : "http://www.w3.org/2001/XMLSchema",
	xsi : "http://www.w3.org/2001/XMLSchema-instance",
	xsl : "http://www.w3.org/1999/XSL/Transform",
	xslt : "http://www.w3.org/1999/XSL/Transform",
	xul : "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
};

/**
 * The ActiveX progid of IE's DOMDocument object.
 * @private
 * @type String
 */
mozile.xml._IE_DOM = null;

/**
 * The ActiveX progid of IE's FreeThreadedDOMDocument object.
 * @private
 * @type String
 */
mozile.xml._IE_THREADEDDOM = null;

/**
 * The ActiveX progid of IE's XSLTemplate object.
 * @private
 * @type String
 */
mozile.xml._IE_XSLTEMPLATE = null;

/**
 * The ActiveX progid of IE's MXXMLWriter object.
 * @private
 * @type String
 */
mozile.xml._IE_XMLWRITER = null;


/**
 * Looks up the prefix corresponding to a namespace in the mozile.xml.ns list.
 * @param {String} namespaceURI The URI of the namespace to find the prefix for.
 * @type String
 */
mozile.xml.lookupPrefix = function(namespaceURI) {
	for(var prefix in mozile.xml.ns) {
		if(mozile.xml.ns[prefix] == namespaceURI) return prefix;
	}
	return null;
}

/**
 * Loads and returns an XML document.
 * This method should work in most browsers.
 * The method checks for a ":" in the filepath. 
 *   If one is found the filepath is treated as an absolute path.
 *   If none is found it treats the filepath as a relative path.
 * <p>This method should work in most browsers.
 * @param {String} string
 * @type Document
 */
mozile.xml.load = function(filepath) {
	//alert("Loading");
	if(typeof(filepath) != "string") return null;

	// Make filepath absolute.
	var uri;
	if(filepath.indexOf(":") > 0) uri = filepath;
	else {
		var loc = location.toString();
		loc = loc.substring(0, loc.lastIndexOf("?"));
		loc = loc.substring(0, loc.lastIndexOf("/") + 1);
		uri = loc + filepath;
	}
	// Hack for accessing local files with Safari
	if(mozile.browser.isSafari) uri = uri.replace("file://", "file:///");

	try { 
		// Try to find an XMLHTTPRequest object
		var XHR = new XMLHttpRequest();
		XHR.overrideMimeType("text/xml");
		XHR.open("GET", uri, false);
		try {
			XHR.send(null);
			return XHR.responseXML;
		} catch(e) {
			mozile.debug.inform("mozile.xml.load", "Error loading document: "+ e);
			return null;
		}
	}
	catch(e) {
		try	{
			// An IE XML load method
			mozile.xml._getDomProgID();
			var xmlDoc = new ActiveXObject(mozile.xml._IE_DOM);
			xmlDoc.async = false;
			try {
				var loaded = xmlDoc.load(uri);
				if(loaded) return xmlDoc;
				else {
					mozile.debug.inform("mozile.xml.load", "Failed to load document.");
				}
			} catch(e) {
				mozile.debug.inform("mozile.xml.load", "Error loading document: "+ mozile.dumpError(e));
			}
		}
		catch(e) {
			mozile.debug.inform("mozile.xml.load", "No XML loading technique avaliable in this browser.");
		}
	}

	return null;
}

/**
 * Parses a string into an XML document, and returns the document.
 * <p>This method should work in most browsers.
 * @param {String} string
 * @type Document
 */
mozile.xml.parse = function(string) {
	if(window.ActiveXObject) {
		mozile.xml._getDomProgID();
		var xmlDoc = new ActiveXObject(mozile.xml._IE_DOM);
		xmlDoc.async = "false";
		xmlDoc.loadXML(string);
		return xmlDoc;
	}
	else if(window.DOMParser) {
		var parser = new DOMParser();
		return parser.parseFromString(string, "text/xml");
	}
	else {
		mozile.debug.inform("mozile.xml.serialize", "No XML parsing technique avaliable in this browser.");
		return null;
	}
}

/**
 * Parses a string into an XML document, and returns the documentElement.
 * <p>This method should work in most browsers.
 * @param {String} string
 * @type Element
 */
mozile.xml.parseElement = function(string) {
	var doc = mozile.xml.parse(string);
	if(doc && doc.documentElement) return doc.documentElement;
	else return null;
}

/**
 * Serializes an XML node (or document) into a string.
 * <p>This method should work in most browsers.
 * @param {Node} node An XML node or document.
 * @type String
 */
mozile.xml.serialize = function(node) {
	if(!node) return null;
	if(node.xml) return node.xml;
	else if(window.XMLSerializer) {
		var serializer = new XMLSerializer()
		return serializer.serializeToString(node);
	}
	else if(node.outerHTML) return node.outerHTML;
	else if(node.innerHTML) {
		var container = document.createElement("container");
		container.appendChild(node.cloneNode(true));
		return container.innerHTML;
	}
	else {
		mozile.debug.inform("mozile.xml.serialize", "No XML serialization technique avaliable in this browser.");
		return null;
	}
}



/**** XSLT Methods ****/

/**
 * Transforms a target document using the given stylesheet document and parameters. Returns a new document.
 * @param {Document} target The document to be transformed.
 * @param {Document} stylesheet The document containing the XSLT to use.
 * @param {Array} parameters Optional. An array of objects with "namespace", "name", and "value" properties.
 * @type Document
 */
mozile.xml.transformToDocument = function(target, stylesheet, parameters) {
	if(!target) return null;
	// Check that the stylesheet is a document.
	if(!stylesheet || stylesheet.nodeType != 9) return null;

	// IE case
	if(window.ActiveXObject) {
		var method = mozile.xml._getMethod(stylesheet);
		var result = mozile.xml._IETransform(target, stylesheet, parameters);
		if(method == "text") result = "<result>"+ result +"</result>";
		return mozile.xml.parse(result);
	}

	// Mozilla case
	if(window.XSLTProcessor != undefined) {
		var processor = new XSLTProcessor();
		processor.importStylesheet(stylesheet);
		mozile.xml._setParameters(processor, parameters);
		return processor.transformToDocument(target);
	}

	else {
		mozile.debug.inform("mozile.xml.transformToDocument", "No XSLT technique avaliable in this browser.");
		return null;
	}

}

/**
 * Transforms a target document using the given stylesheet document and parameters. Returns a new fragment. Since Internet Explorer doens't like moving nodes between documents, if you want to add content to a document using this method.
 * @param {Document} target The document to be transformed.
 * @param {Document} stylesheet The document containing the XSLT to use.
 * @param {Array} parameters Optional. An array of objects with "namespace", "name", and "value" properties.
 * @param {Document} ownerDoc Optional. The document that will own the fragment. If none is giventhe current document is used.
 * @type DocumentFragment
 */
mozile.xml.transformToFragment = function(target, stylesheet, parameters, ownerDoc) {
	if(!target) return null;
	// Check that the stylesheet is a document.
	if(!stylesheet || stylesheet.nodeType != 9) return null;
	if(!ownerDoc) ownerDoc = document;

	// IE case
	if(window.ActiveXObject) {
		var method = mozile.xml._getMethod(stylesheet);
		var result = mozile.xml._IETransform(target, stylesheet, parameters);
		var fragment = ownerDoc.createDocumentFragment();
		// Output fragment
		if(method == "text") fragment.appendChild(ownerDoc.createTextNode(result));
		else if(method == "html" && ownerDoc.body && ownerDoc.body.innerHTML) {
			var container = ownerDoc.createElement("div");
			container.innerHTML = result;
			while(container.childNodes.length)
				fragment.appendChild(container.firstChild);
		}
		else {
			fragment.appendChild(mozile.xml.parseElement(result));
		}
		return fragment;
	}

	// Mozilla case
	if(window.XSLTProcessor != undefined) {
		var processor = new XSLTProcessor();
		processor.importStylesheet(stylesheet);
		mozile.xml._setParameters(processor, parameters);
		return processor.transformToFragment(target, ownerDoc);
	}

	else {
		mozile.debug.inform("mozile.xml.transformToFragment", "No XSLT technique avaliable in this browser.");
		return null;
	}

}

/**
 * Transforms a target document using the given stylesheet document and parameters. Returns a string.
 * @param {Document} target The document to be transformed.
 * @param {Document} stylesheet The document containing the XSLT to use.
 * @param {Array} parameters Optional. An array of objects with "namespace", "name", and "value" properties.
 * @type String
 */
mozile.xml.transformToString = function(target, stylesheet, parameters) {
	if(!target) return null;
	// Check that the stylesheet is a document.
	if(!stylesheet || stylesheet.nodeType != 9) return null;

	// IE case
	if(window.ActiveXObject) {
		return mozile.xml._IETransform(target, stylesheet, parameters);
	}

	// Mozilla case
	if(window.XSLTProcessor != undefined) {
		var processor = new XSLTProcessor();
		processor.importStylesheet(stylesheet);
		mozile.xml._setParameters(processor, parameters);
		var result = processor.transformToDocument(target);
		var source = mozile.xml.serialize(result.documentElement);
		// Strip transformiix tags.
		if(result.documentElement.nodeName == "transformiix:result")
			source = source.substring(78, source.length - 22);
		return source;
	}

	else {
		mozile.debug.inform("mozile.xml.transformToString", "No XSLT technique avaliable in this browser.");
		return null;
	}

}



/**** XSLT Support ****/

/**
 * Gets the output mode of the given XSLT.
 * @private
 * @param {Document} stylesheet The document containing the XSLT to use.
 * @type String
 */
mozile.xml._getMethod = function(stylesheet) {
	var method = "html";
	if(!stylesheet) return method;
	var result;

	// IE Case.
	if(window.ActiveXObject) {
		stylesheet.setProperty("SelectionNamespaces", "xmlns:xsl='http://www.w3.org/1999/XSL/Transform'");
		result = stylesheet.selectSingleNode("//xsl:output");
	}
	
	// Mozilla case
	else if(window.XSLTProcessor != undefined) {
		mozile.require("mozile.xpath");
		var results = mozile.xpath.evaluate("//xsl:output", stylesheet);
    if(results[0]) result = results[0];
	}
	
	// Null case
	else return method;
	
	if(result) method = result.getAttribute("method");
	return method;
}

/**
 * Set parameters for an XSLT processor.
 * @private
 * @param processor The XSLT processor to use.
 * @param {Array} parameters An array of parameter objects with "namespace", "name", and "value" properties.
 * @type Void
 */
mozile.xml._setParameters = function(processor, parameters) {
	if(!processor) return;
	if(!parameters) return;

	var param;
	for(var i=0; i < parameters.length; i++) {
		param = parameters[i];
		if(!param.name || !param.value) continue;

		//Mozilla case
		if(processor.setParameter) {
			processor.setParameter(param.namespace, param.name, param.value);
		}

		// IE case
		else {
			if(param.namespace)
				processor.addParameter(param.name, param.value, param.namespace);
			else processor.addParameter(param.name, param.value);
		}
	}

}

/**
 * Makes sure that a given node is wrapped in a document object.
 * @private
 * @param {Node} node The node to wrap with a document.
 * @type Document
 */
mozile.xml._nodeToDocument = function(node) {
	if(target.nodeType == mozile.dom.DOCUMENT_NODE) return node;
	var doc;

	if(window.ActiveXObject) {
		var source = mozile.xml.serialize(node);
		return mozile.xml.parse(source);
	}
	else if(document.implementation)  {
		doc = document.implementation.createDocument("", "", null);
		var clone = doc.importNode(target, true);
		doc.appendChild(clone);
		return doc;
	}

	else return null;
}

/**
 * Transforms a target document using the given stylesheet document.
 * @private
 * @param {Document} target The document to be transformed.
 * @param {Document} stylesheet The document containing the XSLT to use.
 * @param {Array} parameters Optional. An array of objects with "namespace", "name", and "value" properties.
 * @type String
 */
mozile.xml._IETransform = function(target, stylesheet, parameters) {
	if(!target) return null;
	// Check that the stylesheet is a document.
	if(!stylesheet || stylesheet.nodeType != 9) return null;
	var method, processor, result;

try {
	
	// Initialize and setup the stylesheet document.
	mozile.xml._getDomProgID();
	mozile.xml._getXMLProgIDs();
	var newStyle = new ActiveXObject(mozile.xml._IE_THREADEDDOM);
	newStyle.loadXML(stylesheet.xml);

	// Setup the processor and target document.
	var xslt = new ActiveXObject(mozile.xml._IE_XSLTEMPLATE); 
	xslt.stylesheet = newStyle;
	processor = xslt.createProcessor();
	processor.input = target;
	mozile.xml._setParameters(processor, parameters);

	// Transform.
	processor.transform();
	return processor.output;

} catch(e) {
	return "XSLT ERROR "+ mozile.dumpError(e);
}

}



/**** Internet Explorer Support ****/

/**
 * Takes a list of ActiveX ProgID and returns the first ProgID that is supported by this browser.
 * Borrowed from Sarissa: http://sarissa.sourceforge.net
 * @private
 * @param {Array} idList An array of possible ActiveX Object ids.
 * @type String
 */
mozile.xml._pickRecentProgID = function (idList){
	// found progID flag
	var bFound = false;
	for(var i=0; i < idList.length && !bFound; i++){
			try{
					var oDoc = new ActiveXObject(idList[i]);
					o2Store = idList[i];
					bFound = true;
			}catch (objException){
					// trap; try next progID
			};
	};
	if (!bFound) {
			throw "Could not retreive a valid progID of Class: " + idList[idList.length-1]+". (original exception: "+e+")";
	};
	idList = null;
	return o2Store;
}

/**
 * Find the ProgID for DOMDocument.
 * @private
 * @type Void
 */
mozile.xml._getDomProgID = function() {
	if(!mozile.xml._IE_DOM) {
		mozile.xml._IE_DOM = mozile.xml._pickRecentProgID(["Msxml2.DOMDocument.5.0", "Msxml2.DOMDocument.4.0", "Msxml2.DOMDocument.3.0", "MSXML2.DOMDocument", "MSXML.DOMDocument", "Microsoft.XMLDOM"]);
	}
}

/**
 * Find the ProgIDs for several XML tools.
 * @private
 * @type Void
 */
mozile.xml._getXMLProgIDs = function() {
	if(!mozile.xml._IE_XSLTEMPLATE) {
		mozile.xml._IE_XSLTEMPLATE = mozile.xml._pickRecentProgID(["Msxml2.XSLTemplate.5.0", "Msxml2.XSLTemplate.4.0", "MSXML2.XSLTemplate.3.0"]);
		mozile.xml._IE_THREADEDDOM = mozile.xml._pickRecentProgID(["Msxml2.FreeThreadedDOMDocument.5.0", "MSXML2.FreeThreadedDOMDocument.4.0", "MSXML2.FreeThreadedDOMDocument.3.0"]);
		mozile.xml._IE_XMLWRITER = mozile.xml._pickRecentProgID(["Msxml2.MXXMLWriter.5.0", "Msxml2.MXXMLWriter.4.0", "Msxml2.MXXMLWriter.3.0", "MSXML2.MXXMLWriter", "MSXML.MXXMLWriter", "Microsoft.XMLDOM"]);
	}
}




Documentation generated by JSDoc on Wed Feb 20 13:25:28 2008