////////////////////////////////////////////////////////////////////////////////////////////////////
function getDOMDoc(a_source) {
	var xDoc;
	try {
		xDoc = new ActiveXObject('MSXML2.FreeThreadedDOMDocument.6.0');
		xDoc.setProperty('MultipleErrorMessages', true);
		xDoc.resolveExternals = true;
	} catch(e) {
		xDoc = new ActiveXObject('MSXML2.FreeThreadedDOMDocument.4.0');
	}
	xDoc.setProperty('NewParser', true);
	//xDoc.setProperty('ServerHTTPRequest', true); // requires use of proxycfg.exe
	//xDoc.setProperty('SelectionLanguage', 'XPath'); // defaults to XPath in MSXML 4.0 and higher
	xDoc.async = false;
	xDoc.validateOnParse = true;
	
	var success;
	if(a_source == null) {
		throw new Error('Cannot parse null object!');
	} else if(typeof(a_source) == 'object' && typeof(a_source.xml) != 'undefined') {
		xDoc = a_source;
		success = true;
	} else if(typeof a_source != 'string' || a_source.indexOf('<') == -1) {
		success = xDoc.load(a_source);
	} else {
		success = xDoc.loadXML(a_source);
	}
	if(!success) {
		var e = xDoc.parseError;
		var source = e.url;
		if(source == '' && typeof(a_source) == 'string') {
			if(a_source.indexOf('<') == -1) {
				source = a_source;
			} else {
				source = a_source.substr(0, 255);
				if(a_source.length > 255) {
					source += '...';
				}
			}
		}
		throw new Error('Line ' + e.line + ', column ' + e.linepos + ' (offset ' + e.filepos + ') of "' + source + '":\n'
						+ e.reason + '\n' + e.srcText.substr(0, 500));
	}
	
	return xDoc;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
function transformXML(a_xml, a_xsl, a_output, a_parameters) {
	var xData = getDOMDoc(a_xml);
	var xStyle = getDOMDoc(a_xsl);

	var template;
	try {
		template = new ActiveXObject('MSXML2.XSLTemplate.6.0');
		xStyle.setProperty('AllowXsltScript', true);
		xStyle.setProperty('AllowDocumentFunction', true);
	} catch(e) {
		template = new ActiveXObject('MSXML2.XSLTemplate.4.0');
	}
	template.stylesheet = xStyle;
	
	var processor = template.createProcessor()
	processor.input = xData;
	if(a_output != null) {
		processor.output = a_output;
	}
	
	if(a_parameters != null) {
		for(var key in a_parameters) {
			if(typeof a_parameters[key] != 'function' && a_parameters[key] != null) {
				try {
					processor.addParameter(key, a_parameters[key]);
				} catch(e) {
					throw new Error(e.name + ' while passing parameter \'' + key + '\' to XSL: ' + e.message);
				}
			}
		}
	}
	
	processor.transform();
	
	if(a_output == null) {
		return processor.output;
	}
}


////////////////////////////////////////////////////////////////////////////////////////////////////
function getXMLFormattedDate(a_date, a_includeTimeZone) {

    function f(n) {
        return n < 10 ? '0' + n : n;
    }
	function tz(d) {
		var zone = d.toString().match(/^.*([\+\-]\d\d)(\d\d).*$/);
		return zone ? zone[1] + ':' + zone[2] : '';
	}

    return a_date.getFullYear() + '-' +
           f(a_date.getMonth() + 1) + '-' +
           f(a_date.getDate()) + 'T' +
           f(a_date.getHours()) + ':' +
           f(a_date.getMinutes()) + ':' +
           f(a_date.getSeconds()) + 
		   (a_includeTimeZone ? tz(a_date) : '');
}
