Skip to content

Instantly share code, notes, and snippets.

@pieplu
Last active January 24, 2018 13:54
Show Gist options
  • Save pieplu/6be55d1d8f27ea9b8dcf4de6b1933547 to your computer and use it in GitHub Desktop.
Save pieplu/6be55d1d8f27ea9b8dcf4de6b1933547 to your computer and use it in GitHub Desktop.
/*
* An URI datatype. Based upon examples in RFC3986.
*
* TODO %-escaping
* TODO split apart authority
* TODO split apart query_string (on demand, anyway)
*
* @(#) $Id$
*/
// Constructor for the URI object. Parse a string into its components.
function URI(str) {
if (!str) str = "";
// Based on the regex in RFC2396 Appendix B.
var parser = /^(?:([^:\/?\#]+):)?(?:\/\/([^\/?\#]*))?([^?\#]*)(?:\?([^\#]*))?(?:\#(.*))?/;
var result = str.match(parser);
this.scheme = result[1] || null;
this.authority = result[2] || null;
this.path = result[3] || null;
this.query = result[4] || null;
this.fragment = result[5] || null;
}
// Restore the URI to it's stringy glory.
URI.prototype.toString = function () {
var str = "";
if (this.scheme) {
str += this.scheme + ":";
}
if (this.authority) {
str += "//" + this.authority;
}
if (this.path) {
str += this.path;
}
if (this.query) {
str += "?" + this.query;
}
if (this.fragment) {
str += "#" + this.fragment;
}
return str;
};
// Introduce a new scope to define some private helper functions.
(function () {
// RFC3986 §5.2.3 (Merge Paths)
function merge(base, rel_path) {
var dirname = /^(.*)\//;
if (base.authority && !base.path) {
return "/" + rel_path;
}
else {
return base.path.match(dirname)[0] + rel_path;
}
}
// Match two path segments, where the second is ".." and the first must
// not be "..".
var DoubleDot = /\/((?!\.\.\/)[^\/]*)\/\.\.\//;
function remove_dot_segments(path) {
if (!path) return "";
// Remove any single dots
var newpath = path.replace(/\/\.\//g, '/');
// Remove any trailing single dots.
newpath = newpath.replace(/\/\.$/, '/');
// Remove any double dots and the path previous. NB: We can't use
// the "g", modifier because we are changing the string that we're
// matching over.
while (newpath.match(DoubleDot)) {
newpath = newpath.replace(DoubleDot, '/');
}
// Remove any trailing double dots.
newpath = newpath.replace(/\/([^\/]*)\/\.\.$/, '/');
// If there are any remaining double dot bits, then they're wrong
// and must be nuked. Again, we can't use the g modifier.
while (newpath.match(/\/\.\.\//)) {
newpath = newpath.replace(/\/\.\.\//, '/');
}
return newpath;
}
// RFC3986 §5.2.2. Transform References;
URI.prototype.resolve = function (base) {
var target = new URI();
if (this.scheme) {
target.scheme = this.scheme;
target.authority = this.authority;
target.path = remove_dot_segments(this.path);
target.query = this.query;
}
else {
if (this.authority) {
target.authority = this.authority;
target.path = remove_dot_segments(this.path);
target.query = this.query;
}
else {
// XXX Original spec says "if defined and empty"…;
if (!this.path) {
target.path = base.path;
if (this.query) {
target.query = this.query;
}
else {
target.query = base.query;
}
}
else {
if (this.path.charAt(0) === '/') {
target.path = remove_dot_segments(this.path);
} else {
target.path = merge(base, this.path);
target.path = remove_dot_segments(target.path);
}
target.query = this.query;
}
target.authority = base.authority;
}
target.scheme = base.scheme;
}
target.fragment = this.fragment;
return target;
};
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment