root/urilib.js
| Revision 146 (checked in by fumanchu, 2 years ago) | |
|---|---|
| |
| Line | |
|---|---|
| 1 | // This module provides URI parsing and joining as described |
| 2 | // in RFC 2396. |
| 3 | // |
| 4 | // Usage: |
| 5 | // <script src="urilib.js"></script> |
| 6 | // <script type="text/javascript"> |
| 7 | // base = urilib.parse('http://www.site.example/path/to/app'); |
| 8 | // ref = new urilib.URI('', '', 'thing/5', 'a=1'); |
| 9 | // full = urilib.join(base, url); |
| 10 | // alert(full); // "http://www.site.example/path/to/thing/5?a=1" |
| 11 | // </script> |
| 12 | |
| 13 | var urilib = {}; |
| 14 | |
| 15 | urilib.parse_regex = new RegExp( |
| 16 | // Scheme |
| 17 | "^(?:([a-zA-Z][a-zA-Z0-9+\\-.]*)[:])?" + |
| 18 | // Authority |
| 19 | "(?://([^/]+))?" + |
| 20 | // Path |
| 21 | "([^?]+)?" + |
| 22 | // Query |
| 23 | "([?](.*))?"); |
| 24 | |
| 25 | urilib.AbsoluteURIError = function (message) { |
| 26 | // Error thrown when relativeURI functions are called on an absoluteURI. |
| 27 | this.message = message || "relativeURI functions cannot be called on an absoluteURI"; |
| 28 | } |
| 29 | urilib.AbsoluteURIError.prototype.toString = function () { |
| 30 | return 'AbsoluteURIError: "' + this.message + '"'; |
| 31 | } |
| 32 | |
| 33 | urilib.RelativeURIError = function (message) { |
| 34 | // Error thrown when absoluteURI functions are called on a relativeURI. |
| 35 | this.message = message || "absoluteURI functions cannot be called on a relativeURI"; |
| 36 | } |
| 37 | urilib.RelativeURIError.prototype.toString = function () { |
| 38 | return 'RelativeURIError: "' + this.message + '"'; |
| 39 | } |
| 40 | |
| 41 | urilib.URI = function (scheme, authority, path, query) { |
| 42 | this.scheme = scheme || ""; |
| 43 | this.authority = authority || ""; |
| 44 | this.path = path || ""; |
| 45 | this.query = query || ""; |
| 46 | return this; |
| 47 | }; |
| 48 | |
| 49 | urilib.URI.prototype.toString = function () { |
| 50 | // 7) The resulting URI components, including any inherited from the |
| 51 | // base URI, are recombined to give the absolute form of the URI |
| 52 | // reference. |
| 53 | var result = ""; |
| 54 | if (this.scheme != '') result = this.scheme + ":"; |
| 55 | if (this.authority != '') result = result + "//" + this.authority; |
| 56 | result = result + this.path; |
| 57 | if (this.query != '') result = result + "?" + this.query; |
| 58 | return result; |
| 59 | }; |
| 60 | |
| 61 | urilib.URI.prototype.is_relative = function () { |
| 62 | return (this.path.length > 0 && this.path.charAt(0) != '/'); |
| 63 | } |
| 64 | |
| 65 | urilib.URI.prototype.net_path = function () { |
| 66 | // Return the 'net_path' ("//" authority [ abs_path ]) of this URI. |
| 67 | if (this.is_relative()) throw urilib.RelativeURIError(); |
| 68 | return "//" + this.authority + this.path; |
| 69 | }; |
| 70 | |
| 71 | urilib.URI.prototype.abs_path = function () { |
| 72 | // Return the 'abs_path' ("/" path_segments) of this URI. |
| 73 | if (this.is_relative()) throw urilib.RelativeURIError(); |
| 74 | return this.path; |
| 75 | }; |
| 76 | |
| 77 | urilib.URI.prototype.rel_path = function () { |
| 78 | // Return the 'rel_path' (rel_segment [ abs_path ]) of this URI. |
| 79 | if (!this.is_relative()) throw urilib.AbsoluteURIError(); |
| 80 | return this.path; |
| 81 | }; |
| 82 | |
| 83 | urilib.URI.prototype.hier_part = function (include_authority) { |
| 84 | // Return the 'hier_part' ( net_path | abs_path ) [ "?" query ] of this URI. |
| 85 | // |
| 86 | // If 'include_authority' is false (the default), returns the |
| 87 | // abs_path [+ query] by omitting the authority component. |
| 88 | // Otherwise, returns the net_path [+ query] by including the |
| 89 | // authority component. |
| 90 | if (this.is_relative()) throw urilib.RelativeURIError(); |
| 91 | var result = include_authority ? this.net_path() : this.path; |
| 92 | if (this.query != '') result = result + "?" + this.query; |
| 93 | return result; |
| 94 | }; |
| 95 | |
| 96 | urilib.URI.prototype.server_relative = function () { |
| 97 | // Return the 'server relative' ( abs_path | rel_path ) [ "?" query ] form of this URI. |
| 98 | var result = this.path; |
| 99 | if (this.query != '') result = result + "?" + this.query; |
| 100 | return result; |
| 101 | }; |
| 102 | |
| 103 | urilib.parse = function (uri) { |
| 104 | // Return a URI object populated by parsing the given uri string. |
| 105 | uri = uri || ""; |
| 106 | var atoms = urilib.parse_regex.exec(uri); |
| 107 | return new urilib.URI(atoms[1], atoms[2], atoms[3], atoms[4]); |
| 108 | }; |
| 109 | |
| 110 | urilib.normalize = function (path) { |
| 111 | // Return 'path', normalized to remove '.' and '<segment>/..' segments. |
| 112 | // |
| 113 | // 'path' may be either a string or an Array. |
| 114 | // The return type will match the input type. |
| 115 | var typ = typeof(path); |
| 116 | if (typ == 'string') path = path.split("/"); |
| 117 | |
| 118 | // 5c) All occurrences of "./", where "." is a complete path segment, |
| 119 | // are removed from the buffer string. |
| 120 | // 5d) If the buffer string ends with "." as a complete path segment, |
| 121 | // that "." is removed. |
| 122 | var pos = 0; |
| 123 | while (pos < path.length) { |
| 124 | if (path[pos] == '.') { |
| 125 | path.splice(pos, 1); |
| 126 | } else { |
| 127 | pos++; |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | // 5e) All occurrences of "<segment>/../", where <segment> is a |
| 132 | // complete path segment not equal to "..", are removed from the |
| 133 | // buffer string. Removal of these path segments is performed |
| 134 | // iteratively, removing the leftmost matching pattern on each |
| 135 | // iteration, until no matching pattern remains. |
| 136 | // 5f) If the buffer string ends with "<segment>/..", where <segment> |
| 137 | // is a complete path segment not equal to "..", that |
| 138 | // "<segment>/.." is removed. |
| 139 | pos = 1; |
| 140 | while (pos < path.length) { |
| 141 | if (pos > 0 && path[pos] == '..' && path[pos - 1] != '..') { |
| 142 | path.splice(pos - 1, 2); |
| 143 | pos--; |
| 144 | } else { |
| 145 | pos++; |
| 146 | } |
| 147 | } |
| 148 | |
| 149 | // g) If the resulting buffer string still begins with one or more |
| 150 | // complete path segments of "..", then the reference is |
| 151 | // considered to be in error. |
| 152 | if (path.length > 0 && path[0] == '..') |
| 153 | throw ("Could not normalize. URI begins with illegal '..' segment." + |
| 154 | path.join('/')); |
| 155 | |
| 156 | if (typ == 'string') path = path.join("/"); |
| 157 | return path; |
| 158 | }; |
| 159 | |
| 160 | urilib.join = function (base, ref) { |
| 161 | // Return a URI object which combines the two given URI objects. |
| 162 | |
| 163 | // 1) The URI reference is parsed into the potential four components |
| 164 | // and fragment identifier, as described in Section 4.3. |
| 165 | if (base.constructor != urilib.URI) base = urilib.parse(base); |
| 166 | if (ref.constructor != urilib.URI) ref = urilib.parse(ref); |
| 167 | |
| 168 | // 2) If the path component is empty and the scheme, authority, and |
| 169 | // query components are undefined, then it is a reference to the |
| 170 | // current document and we are done. |
| 171 | if (ref.path == '' && ref.scheme == '' && |
| 172 | ref.authority == '' && ref.query == '') return base; |
| 173 | |
| 174 | // 3) If the scheme component is defined, indicating that the reference |
| 175 | // starts with a scheme name, then the reference is interpreted as an |
| 176 | // absolute URI and we are done. |
| 177 | if (ref.scheme != '') return ref; |
| 178 | |
| 179 | // 2b) Otherwise, the reference URI's |
| 180 | // query and fragment components are defined as found (or not found) |
| 181 | // within the URI reference and not inherited from the base URI. |
| 182 | // 3b) Otherwise, the reference URI's |
| 183 | // scheme is inherited from the base URI's scheme component. |
| 184 | var newUri = new urilib.URI(base.scheme, "", "", ref.query); |
| 185 | |
| 186 | // 4) If the authority component is defined, then the reference is a |
| 187 | // network-path and we skip to step 7. |
| 188 | if (ref.authority == '') { |
| 189 | // Otherwise, the reference |
| 190 | // URI's authority is inherited from the base URI's authority |
| 191 | // component, which will also be undefined if the URI scheme does not |
| 192 | // use an authority component. |
| 193 | newUri.authority = base.authority; |
| 194 | |
| 195 | // 5) If the path component begins with a slash character ("/"), then |
| 196 | // the reference is an absolute-path and we skip to step 7. |
| 197 | if (ref.path.indexOf('/') != 0) { |
| 198 | // 6) If this step is reached, then we are resolving a relative-path |
| 199 | // reference. The relative path needs to be merged with the base |
| 200 | // URI's path. |
| 201 | // |
| 202 | // a) All but the last segment of the base URI's path component is |
| 203 | // copied to the buffer. In other words, any characters after the |
| 204 | // last (right-most) slash character, if any, are excluded. |
| 205 | var buf = base.path; |
| 206 | var last_slash = buf.lastIndexOf('/'); |
| 207 | if (last_slash >= 0) buf = buf.substring(0, last_slash + 1); |
| 208 | |
| 209 | // b) The reference's path component is appended to the buffer string. |
| 210 | buf = buf + ref.path; |
| 211 | |
| 212 | // c) through g) -- see normalize() |
| 213 | // h) The remaining buffer string is the reference URI's new path |
| 214 | // component. |
| 215 | newUri.path = urilib.normalize(buf); |
| 216 | } |
| 217 | } |
| 218 | return newUri; |
| 219 | } |
| 220 |
Note: See TracBrowser for help on using the browser.
