uri: Move exports to the end of the file.
6 * See: http://hg.grauw.nl/grauw-lib/file/tip/src/uri.js
8 * @author Laurens Holst (http://www.grauw.nl/)
10 * Copyright 2010 Laurens Holst
12 * Licensed under the Apache License, Version 2.0 (the "License");
13 * you may not use this file except in compliance with the License.
14 * You may obtain a copy of the License at
16 * http://www.apache.org/licenses/LICENSE-2.0
18 * Unless required by applicable law or agreed to in writing, software
19 * distributed under the License is distributed on an "AS IS" BASIS,
20 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 * See the License for the specific language governing permissions and
22 * limitations under the License.
25 gl.module('gl.uri', [], function() {
28 * Constructs a URI object.
30 * @class Implementation of URI parsing and base URI resolving algorithm in RFC 3986.
31 * @param {string|URI} uri A string or URI object to create the object from.
33 var URI = function(vURI) {
34 if (vURI instanceof URI) { // copy constructor
35 this.scheme = vURI.scheme;
36 this.authority = vURI.authority;
37 this.path = vURI.path;
38 this.query = vURI.query;
39 this.fragment = vURI.fragment;
40 } else if (vURI) { // vURI is URI string or cast to string
41 var c = oParseRegex.exec(vURI);
43 this.authority = c[2];
50 // Initial values on the prototype
51 URI.prototype.scheme = null;
52 URI.prototype.authority = null;
53 URI.prototype.path = '';
54 URI.prototype.query = null;
55 URI.prototype.fragment = null;
57 // Regular expression from RFC 2396 appendix B
58 var oParseRegex = new RegExp('^(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(?:\\?([^#]*))?(?:#(.*))?');
61 * Resolves a relative URI against an absolute base URI.
63 * @param {String} uri the relative URI to resolve
64 * @param {String} baseURI the base URI (must be absolute) to resolve against
66 URI.resolve = function(sURI, sBaseURI) {
67 var oURI = hCache[sURI] || (hCache[sURI] = new URI(sURI));
68 var oBaseURI = hCache[sBaseURI] || (hCache[sBaseURI] = new URI(sBaseURI));
69 return oURI.resolve(oBaseURI).toString();
75 * Tests whether the URI is an absolute URI.
76 * See RFC 3986 section 4.3.
78 URI.prototype.isAbsolute = function() {
79 return this.scheme && !this.fragment;
83 //* Extensive validation of the URI against the ABNF in RFC 3986
85 //URI.prototype.validate
88 * Tests whether the URI is a same-document reference.
89 * See RFC 3986 section 4.4.
91 * To perform more thorough comparison, you can normalise the URI objects.
93 URI.prototype.isSameDocumentAs = function(oURI) {
94 return oURI.scheme == this.scheme &&
95 oURI.authority == this.authority &&
96 oURI.path == this.path &&
97 oURI.query == this.query;
101 * Simple String Comparison of two URIs.
102 * See RFC 3986 section 6.2.1.
104 * To perform more thorough comparison, you can normalise the URI objects.
106 URI.prototype.equals = function(oURI) {
107 return this.isSameDocumentAs(oURI) && oURI.fragment == this.fragment;
111 * Normalizes the URI using syntax-based normalization.
112 * This includes case normalization, percent-encoding normalization and path segment normalization.
113 * XXX: Percent-encoding normalization does not escape characters that need to be escaped.
114 * (Although that would not be a valid URI in the first place. See validate().)
115 * See RFC 3986 section 6.2.2.
117 URI.prototype.normalize = function() {
118 this.removeDotSegments();
120 this.scheme = this.scheme.toLowerCase();
122 this.authority = this.authority.replace(oAuthorityRegex, fAuthority).
123 replace(oCaseRegex, fCase);
125 this.path = this.path.replace(oCaseRegex, fCase);
127 this.query = this.query.replace(oCaseRegex, fCase);
129 this.fragment = this.fragment.replace(oCaseRegex, fCase);
132 var oCaseRegex = /%[0-9a-z]{2}/gi;
133 var oPercentRegex = /[a-zA-Z0-9\-\._~]/;
134 var oAuthorityRegex = /(.*@)?([^@:]*)(:.*)?/;
136 function fCase(sStr) {
137 var sDec = unescape(sStr);
138 return oPercentRegex.test(sDec) ? sDec : sStr.toUpperCase();
141 function fAuthority(sStr, p1, p2, p3) {
142 return (p1 || '') + p2.toLowerCase() + (p3 || '');
146 * Resolve a relative URI (this) against a base URI.
147 * The base URI must be an absolute URI.
148 * See RFC 3986 section 5.2
150 URI.prototype.resolve = function(oBaseURI) {
151 var oURI = new URI();
153 oURI.scheme = this.scheme;
154 oURI.authority = this.authority;
155 oURI.path = this.path;
156 oURI.query = this.query;
158 oURI.scheme = oBaseURI.scheme;
159 if (this.authority) {
160 oURI.authority = this.authority;
161 oURI.path = this.path;
162 oURI.query = this.query;
164 oURI.authority = oBaseURI.authority;
165 if (this.path == '') {
166 oURI.path = oBaseURI.path;
167 oURI.query = this.query || oBaseURI.query;
169 if (this.path.charAt(0) == '/') {
170 oURI.path = this.path;
171 oURI.removeDotSegments();
173 if (oBaseURI.authority && oBaseURI.path == '') {
174 oURI.path = '/' + this.path;
176 oURI.path = oBaseURI.path.substring(0, oBaseURI.path.lastIndexOf('/') + 1) + this.path;
178 oURI.removeDotSegments();
180 oURI.query = this.query;
184 oURI.fragment = this.fragment;
189 * Remove dot segments from path.
190 * See RFC 3986 section 5.2.4
192 URI.prototype.removeDotSegments = function() {
193 var aInput = this.path.split('/'),
196 bAbsPath = aInput[0] == '';
199 var sFirst = aInput[0] == '' ? aInput.shift() : null;
200 while (aInput.length) {
201 sSegment = aInput.shift();
202 if (sSegment == '..') {
204 } else if (sSegment != '.') {
205 aOutput.push(sSegment);
208 if (sSegment == '.' || sSegment == '..')
212 this.path = aOutput.join('/');
216 * Serialises the URI to a string.
218 URI.prototype.toString = function() {
221 sResult += this.scheme + ':';
223 sResult += '//' + this.authority;
224 sResult += this.path;
226 sResult += '?' + this.query;
228 sResult += '#' + this.fragment;