xml:base support in old browsers

Before using this code, check if your browser has native support for Node.baseURI.

These two functions aim to allow some support for xml:base.

Note however, that this code is not based on a thorough examination of the specs related to forming base URIs and could well have a number of errors. However, it is working in some of the cases tested.

var scheme = /(\w(\w|\d|\+|\- |\ .)*)\:\/\//;
function getXmlBaseLink (xlink, thisitem) {
	var xmlbase = '';
	if (!xlink.match(scheme)) { // Only check for XML Base if there is no protocol // tests for 'scheme' per http://www.ietf.org/rfc/rfc2396.txt'
		xmlbase = getXmlBase (thisitem);
		if (!xmlbase.match(/\/$/) && !xlink.match(/\/$/)) { // If no preceding slash on XLink or trailing slash on xml:base, add one in between
			xmlbase += '/';
		}
		else if (xmlbase.match(/\/$/) && xlink.match(/\/$/)) {
			xmlbase = xmlbase.substring(0, xmlbase.length-2); // Strip off last slash to join with XLink path with slash
		}
//                        alert(xmlbase + '::' + xlink);
	}

	var link = xmlbase + xlink;
	if (!link.match(scheme)) { // If there is no domain, we'll need to use the current domain
		var loc = window.location;
		if (link.indexOf('/') === 0 ) { // If link is an absolute URL, it should be from the host only
			link = loc.protocol + '//' + loc.host + link;
		}
		else { // If link is relative, it should be from full path, minus the file
			var dirpath = loc.pathname.substring(0, loc.pathname.lastIndexOf('/')-1);
			if (link.lastIndexOf('/') !== link.length-1) {
				link += '/';
			}
			link = loc.protocol + '//' + loc.host + dirpath + link;
		}
	}

	return link;
}
function getXmlBase (thisitem) {
	// Fix: Need to keep going up the chain if still a relative URL!!!!!
	// var ns = 'http://www.w3.org/XML/1998/namespace';
	var att, protocolPos;
	var xmlbase = '';
	var abs = false;
	// Avoid loop if node is not present
	if (!thisitem || !thisitem.nodeName) {
		return xmlbase;
	}
	// CHECK PRESENT ELEMENT AND HIGHER UP FOR XML:BASE
	// Now check for the next matching local name up in the hierarchy (until the document root)
	while (thisitem.nodeName !== '#document' && thisitem.nodeName !== '#document-fragment')  {
		att = thisitem.getAttribute('xml:base'); // xml: namespaces MUST use 'xml' prefix
		if (att) {
			protocolPos = att.indexOf('//');
			var protocolMatch = att.match(scheme);
			if (protocolMatch) { // if has protocol, can stop
				if (abs) {
					var skipfile = (att.indexOf('///') === protocolPos) ? 3 : 2; // If the file protocol has an extra slashe, prepare to also skip it in the separator search
					var att2 = att.indexOf('/', protocolPos+skipfile); // Find first path separator ('/') after protocol
					if (att2 !== -1) {
						att = att.substring(0, att2 - 1); // Don't want any trailing slash, as the absolute path to be added already has one
					}
				}
				else if (!att.match(/\/$/)) { // If no trailing slash, add one, since it is being attached to a relative path
					att += '/';
				}
				xmlbase = att + xmlbase; // If previous path was not absolute, resolve against the full URI here'
				break;
			}
			else if (att.indexOf('/') === 0) { // if absolute (/), need to prepare for next time to strip out after slash
				xmlbase = att + xmlbase;
				abs = true; // Once the protocol is found on the next round, make sure any extra path is ignored
			}
			else { // if relative, just add it
				xmlbase = att + xmlbase;
			}
		}
		thisitem = thisitem.parentNode;
	}
	//return (xmlbase === '') ? null : xmlbase;
	return xmlbase;
}