// 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; }