You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
274 lines
7.4 KiB
JavaScript
274 lines
7.4 KiB
JavaScript
/**
|
|
* @license
|
|
*
|
|
* Grauw URI utilities
|
|
*
|
|
* See: http://hg.grauw.nl/grauw-lib/file/tip/src/uri.js
|
|
*
|
|
* @author Laurens Holst (http://www.grauw.nl/)
|
|
*
|
|
* Copyright 2012 Laurens Holst
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* Constructs a URI object.
|
|
* @constructor
|
|
* @class Implementation of URI parsing and base URI resolving algorithm in RFC 3986.
|
|
* @param {string|URI} uri A string or URI object to create the object from.
|
|
*/
|
|
function URI(uri) {
|
|
if (uri instanceof URI) { // copy constructor
|
|
this.scheme = uri.scheme;
|
|
this.authority = uri.authority;
|
|
this.path = uri.path;
|
|
this.query = uri.query;
|
|
this.fragment = uri.fragment;
|
|
} else if (uri) { // uri is URI string or cast to string
|
|
var c = parseRegex.exec(uri);
|
|
this.scheme = c[1];
|
|
this.authority = c[2];
|
|
this.path = c[3];
|
|
this.query = c[4];
|
|
this.fragment = c[5];
|
|
}
|
|
}
|
|
// Initial values on the prototype
|
|
URI.prototype.scheme = null;
|
|
URI.prototype.authority = null;
|
|
URI.prototype.path = '';
|
|
URI.prototype.query = null;
|
|
URI.prototype.fragment = null;
|
|
|
|
// Regular expression from RFC 3986 appendix B
|
|
var parseRegex = new RegExp('^(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(?:\\?([^#]*))?(?:#(.*))?$');
|
|
|
|
/**
|
|
* Returns the scheme part of the URI.
|
|
* In "http://example.com:80/a/b?x#y" this is "http".
|
|
*/
|
|
URI.prototype.getScheme = function() {
|
|
return this.scheme;
|
|
};
|
|
|
|
/**
|
|
* Returns the authority part of the URI.
|
|
* In "http://example.com:80/a/b?x#y" this is "example.com:80".
|
|
*/
|
|
URI.prototype.getAuthority = function() {
|
|
return this.authority;
|
|
};
|
|
|
|
/**
|
|
* Returns the path part of the URI.
|
|
* In "http://example.com:80/a/b?x#y" this is "/a/b".
|
|
* In "mailto:mike@example.com" this is "mike@example.com".
|
|
*/
|
|
URI.prototype.getPath = function() {
|
|
return this.path;
|
|
};
|
|
|
|
/**
|
|
* Returns the query part of the URI.
|
|
* In "http://example.com:80/a/b?x#y" this is "x".
|
|
*/
|
|
URI.prototype.getQuery = function() {
|
|
return this.query;
|
|
};
|
|
|
|
/**
|
|
* Returns the fragment part of the URI.
|
|
* In "http://example.com:80/a/b?x#y" this is "y".
|
|
*/
|
|
URI.prototype.getFragment = function() {
|
|
return this.fragment;
|
|
};
|
|
|
|
/**
|
|
* Tests whether the URI is an absolute URI.
|
|
* See RFC 3986 section 4.3.
|
|
*/
|
|
URI.prototype.isAbsolute = function() {
|
|
return !!this.scheme && !this.fragment;
|
|
};
|
|
|
|
///**
|
|
//* Extensive validation of the URI against the ABNF in RFC 3986
|
|
//*/
|
|
//URI.prototype.validate
|
|
|
|
/**
|
|
* Tests whether the URI is a same-document reference.
|
|
* See RFC 3986 section 4.4.
|
|
*
|
|
* To perform more thorough comparison, you can normalise the URI objects.
|
|
*/
|
|
URI.prototype.isSameDocumentAs = function(uri) {
|
|
return uri.scheme == this.scheme &&
|
|
uri.authority == this.authority &&
|
|
uri.path == this.path &&
|
|
uri.query == this.query;
|
|
};
|
|
|
|
/**
|
|
* Simple String Comparison of two URIs.
|
|
* See RFC 3986 section 6.2.1.
|
|
*
|
|
* To perform more thorough comparison, you can normalise the URI objects.
|
|
*/
|
|
URI.prototype.equals = function(uri) {
|
|
return this.isSameDocumentAs(uri) && uri.fragment == this.fragment;
|
|
};
|
|
|
|
/**
|
|
* Normalizes the URI using syntax-based normalization.
|
|
* This includes case normalization, percent-encoding normalization and path segment normalization.
|
|
* XXX: Percent-encoding normalization does not escape characters that need to be escaped.
|
|
* (Although that would not be a valid URI in the first place. See validate().)
|
|
* See RFC 3986 section 6.2.2.
|
|
*/
|
|
URI.prototype.normalize = function() {
|
|
this.removeDotSegments();
|
|
if (this.scheme)
|
|
this.scheme = this.scheme.toLowerCase();
|
|
if (this.authority)
|
|
this.authority = this.authority.replace(authorityRegex, replaceAuthority).
|
|
replace(caseRegex, replaceCase);
|
|
if (this.path)
|
|
this.path = this.path.replace(caseRegex, replaceCase);
|
|
if (this.query)
|
|
this.query = this.query.replace(caseRegex, replaceCase);
|
|
if (this.fragment)
|
|
this.fragment = this.fragment.replace(caseRegex, replaceCase);
|
|
};
|
|
|
|
var caseRegex = /%[0-9a-z]{2}/gi;
|
|
var percentRegex = /[a-zA-Z0-9\-\._~]/;
|
|
var authorityRegex = /(.*@)?([^@:]*)(:.*)?/;
|
|
|
|
function replaceCase(str) {
|
|
var dec = unescape(str);
|
|
return percentRegex.test(dec) ? dec : str.toUpperCase();
|
|
}
|
|
|
|
function replaceAuthority(str, p1, p2, p3) {
|
|
return (p1 || '') + p2.toLowerCase() + (p3 || '');
|
|
}
|
|
|
|
/**
|
|
* Resolve a relative URI (this) against a base URI.
|
|
* The base URI must be an absolute URI.
|
|
* See RFC 3986 section 5.2
|
|
*/
|
|
URI.prototype.resolve = function(baseURI) {
|
|
var uri = new URI();
|
|
if (this.scheme) {
|
|
uri.scheme = this.scheme;
|
|
uri.authority = this.authority;
|
|
uri.path = this.path;
|
|
uri.query = this.query;
|
|
} else {
|
|
uri.scheme = baseURI.scheme;
|
|
if (this.authority) {
|
|
uri.authority = this.authority;
|
|
uri.path = this.path;
|
|
uri.query = this.query;
|
|
} else {
|
|
uri.authority = baseURI.authority;
|
|
if (this.path == '') {
|
|
uri.path = baseURI.path;
|
|
uri.query = this.query || baseURI.query;
|
|
} else {
|
|
if (this.path.charAt(0) == '/') {
|
|
uri.path = this.path;
|
|
uri.removeDotSegments();
|
|
} else {
|
|
if (baseURI.authority && baseURI.path == '') {
|
|
uri.path = '/' + this.path;
|
|
} else {
|
|
uri.path = baseURI.path.substring(0, baseURI.path.lastIndexOf('/') + 1) + this.path;
|
|
}
|
|
uri.removeDotSegments();
|
|
}
|
|
uri.query = this.query;
|
|
}
|
|
}
|
|
}
|
|
uri.fragment = this.fragment;
|
|
return uri;
|
|
};
|
|
|
|
/**
|
|
* Remove dot segments from path.
|
|
* See RFC 3986 section 5.2.4
|
|
* @private
|
|
*/
|
|
URI.prototype.removeDotSegments = function() {
|
|
var input = this.path.split('/'),
|
|
output = [],
|
|
segment,
|
|
absPath = input[0] == '';
|
|
if (absPath)
|
|
input.shift();
|
|
var sFirst = input[0] == '' ? input.shift() : null;
|
|
while (input.length) {
|
|
segment = input.shift();
|
|
if (segment == '..') {
|
|
output.pop();
|
|
} else if (segment != '.') {
|
|
output.push(segment);
|
|
}
|
|
}
|
|
if (segment == '.' || segment == '..')
|
|
output.push('');
|
|
if (absPath)
|
|
output.unshift('');
|
|
this.path = output.join('/');
|
|
};
|
|
|
|
// We don't like this function because it builds up a cache that is never cleared.
|
|
// /**
|
|
// * Resolves a relative URI against an absolute base URI.
|
|
// * Convenience method.
|
|
// * @param {String} uri the relative URI to resolve
|
|
// * @param {String} baseURI the base URI (must be absolute) to resolve against
|
|
// */
|
|
// URI.resolve = function(sURI, sBaseURI) {
|
|
// var uri = cache[sURI] || (cache[sURI] = new URI(sURI));
|
|
// var baseURI = cache[sBaseURI] || (cache[sBaseURI] = new URI(sBaseURI));
|
|
// return uri.resolve(baseURI).toString();
|
|
// };
|
|
|
|
// var cache = {};
|
|
|
|
/**
|
|
* Serialises the URI to a string.
|
|
*/
|
|
URI.prototype.toString = function() {
|
|
var result = '';
|
|
if (this.scheme)
|
|
result += this.scheme + ':';
|
|
if (this.authority)
|
|
result += '//' + this.authority;
|
|
result += this.path;
|
|
if (this.query)
|
|
result += '?' + this.query;
|
|
if (this.fragment)
|
|
result += '#' + this.fragment;
|
|
return result;
|
|
};
|
|
|
|
export default URI;
|