// This module provides URI parsing and joining as described
// in RFC 2396.
//
// Usage:
//
//
var urilib = {};
urilib.parse_regex = new RegExp(
// Scheme
"^(?:([a-zA-Z][a-zA-Z0-9+\\-.]*)[:])?" +
// Authority
"(?://([^/]+))?" +
// Path
"([^?]+)?" +
// Query
"([?](.*))?");
urilib.AbsoluteURIError = function (message) {
// Error thrown when relativeURI functions are called on an absoluteURI.
this.message = message || "relativeURI functions cannot be called on an absoluteURI";
}
urilib.AbsoluteURIError.prototype.toString = function () {
return 'AbsoluteURIError: "' + this.message + '"';
}
urilib.RelativeURIError = function (message) {
// Error thrown when absoluteURI functions are called on a relativeURI.
this.message = message || "absoluteURI functions cannot be called on a relativeURI";
}
urilib.RelativeURIError.prototype.toString = function () {
return 'RelativeURIError: "' + this.message + '"';
}
urilib.URI = function (scheme, authority, path, query) {
this.scheme = scheme || "";
this.authority = authority || "";
this.path = path || "";
this.query = query || "";
return this;
};
urilib.URI.prototype.toString = function () {
// 7) The resulting URI components, including any inherited from the
// base URI, are recombined to give the absolute form of the URI
// reference.
var result = "";
if (this.scheme != '') result = this.scheme + ":";
if (this.authority != '') result = result + "//" + this.authority;
result = result + this.path;
if (this.query != '') result = result + "?" + this.query;
return result;
};
urilib.URI.prototype.is_relative = function () {
return (this.path.length > 0 && this.path.charAt(0) != '/');
}
urilib.URI.prototype.net_path = function () {
// Return the 'net_path' ("//" authority [ abs_path ]) of this URI.
if (this.is_relative()) throw urilib.RelativeURIError();
return "//" + this.authority + this.path;
};
urilib.URI.prototype.abs_path = function () {
// Return the 'abs_path' ("/" path_segments) of this URI.
if (this.is_relative()) throw urilib.RelativeURIError();
return this.path;
};
urilib.URI.prototype.rel_path = function () {
// Return the 'rel_path' (rel_segment [ abs_path ]) of this URI.
if (!this.is_relative()) throw urilib.AbsoluteURIError();
return this.path;
};
urilib.URI.prototype.hier_part = function (include_authority) {
// Return the 'hier_part' ( net_path | abs_path ) [ "?" query ] of this URI.
//
// If 'include_authority' is false (the default), returns the
// abs_path [+ query] by omitting the authority component.
// Otherwise, returns the net_path [+ query] by including the
// authority component.
if (this.is_relative()) throw urilib.RelativeURIError();
var result = include_authority ? this.net_path() : this.path;
if (this.query != '') result = result + "?" + this.query;
return result;
};
urilib.URI.prototype.server_relative = function () {
// Return the 'server relative' ( abs_path | rel_path ) [ "?" query ] form of this URI.
var result = this.path;
if (this.query != '') result = result + "?" + this.query;
return result;
};
urilib.parse = function (uri) {
// Return a URI object populated by parsing the given uri string.
uri = uri || "";
var atoms = urilib.parse_regex.exec(uri);
return new urilib.URI(atoms[1], atoms[2], atoms[3], atoms[4]);
};
urilib.normalize = function (path) {
// Return 'path', normalized to remove '.' and '/..' segments.
//
// 'path' may be either a string or an Array.
// The return type will match the input type.
var typ = typeof(path);
if (typ == 'string') path = path.split("/");
// 5c) All occurrences of "./", where "." is a complete path segment,
// are removed from the buffer string.
// 5d) If the buffer string ends with "." as a complete path segment,
// that "." is removed.
var pos = 0;
while (pos < path.length) {
if (path[pos] == '.') {
path.splice(pos, 1);
} else {
pos++;
}
}
// 5e) All occurrences of "/../", where is a
// complete path segment not equal to "..", are removed from the
// buffer string. Removal of these path segments is performed
// iteratively, removing the leftmost matching pattern on each
// iteration, until no matching pattern remains.
// 5f) If the buffer string ends with "/..", where
// is a complete path segment not equal to "..", that
// "/.." is removed.
pos = 1;
while (pos < path.length) {
if (pos > 0 && path[pos] == '..' && path[pos - 1] != '..') {
path.splice(pos - 1, 2);
pos--;
} else {
pos++;
}
}
// g) If the resulting buffer string still begins with one or more
// complete path segments of "..", then the reference is
// considered to be in error.
if (path.length > 0 && path[0] == '..')
throw ("Could not normalize. URI begins with illegal '..' segment." +
path.join('/'));
if (typ == 'string') path = path.join("/");
return path;
};
urilib.join = function (base, ref) {
// Return a URI object which combines the two given URI objects.
// 1) The URI reference is parsed into the potential four components
// and fragment identifier, as described in Section 4.3.
if (base.constructor != urilib.URI) base = urilib.parse(base);
if (ref.constructor != urilib.URI) ref = urilib.parse(ref);
// 2) If the path component is empty and the scheme, authority, and
// query components are undefined, then it is a reference to the
// current document and we are done.
if (ref.path == '' && ref.scheme == '' &&
ref.authority == '' && ref.query == '') return base;
// 3) If the scheme component is defined, indicating that the reference
// starts with a scheme name, then the reference is interpreted as an
// absolute URI and we are done.
if (ref.scheme != '') return ref;
// 2b) Otherwise, the reference URI's
// query and fragment components are defined as found (or not found)
// within the URI reference and not inherited from the base URI.
// 3b) Otherwise, the reference URI's
// scheme is inherited from the base URI's scheme component.
var newUri = new urilib.URI(base.scheme, "", "", ref.query);
// 4) If the authority component is defined, then the reference is a
// network-path and we skip to step 7.
if (ref.authority == '') {
// Otherwise, the reference
// URI's authority is inherited from the base URI's authority
// component, which will also be undefined if the URI scheme does not
// use an authority component.
newUri.authority = base.authority;
// 5) If the path component begins with a slash character ("/"), then
// the reference is an absolute-path and we skip to step 7.
if (ref.path.indexOf('/') != 0) {
// 6) If this step is reached, then we are resolving a relative-path
// reference. The relative path needs to be merged with the base
// URI's path.
//
// a) All but the last segment of the base URI's path component is
// copied to the buffer. In other words, any characters after the
// last (right-most) slash character, if any, are excluded.
var buf = base.path;
var last_slash = buf.lastIndexOf('/');
if (last_slash >= 0) buf = buf.substring(0, last_slash + 1);
// b) The reference's path component is appended to the buffer string.
buf = buf + ref.path;
// c) through g) -- see normalize()
// h) The remaining buffer string is the reference URI's new path
// component.
newUri.path = urilib.normalize(buf);
}
}
return newUri;
}