Create a gist now

Instantly share code, notes, and snippets.

@nutbread /URLParser.js Secret
Created Feb 11, 2015

Embed
A class to parse and resolve URLs
var URLParser = (function () {
// Return class
var URLParser = function (url_str) {
this.hash = "";
this.host = null;
this.hostname = null;
this.pathname = "";
this.protocol = null;
this.search = "";
this.slashes = false;
this.auth = null;
this.port = null;
if (typeof(url_str) === "string") {
this.parse(url_str);
}
};
// Regex
var re_protocol = /^[a-z][a-z0-9\+\-\.]*:/i,
re_double_slash = /^\/{2}/,
re_auth = /^(.*)@/i,
re_port = /:(\d*)$/i,
re_hierarchy_sep = /([\/\?\#]|$)/,
re_path_sep = /([\?\#]|$)/,
re_search_sep = /([\#]|$)/;
// Path operations
var normalize_path = function (path) {
return path;
};
var join_paths = function (base, ext) {
if (ext[0] === "/") {
// Absolute
return ext;
}
// Joined
var p = base.lastIndexOf("/");
if (p < 0) {
// Absolute
return ext;
}
// Join
return base.substr(0, p + 1) + ext;
};
// Functions
URLParser.prototype = {
constructor: URLParser,
parse: function (url_str, ignore_double_slash) {
// Setup object
var m, url_part;
// Protocol
if ((m = re_protocol.exec(url_str)) !== null) {
this.protocol = m[0].toLowerCase();
url_str = url_str.substr(m[0].length);
}
else {
this.protocol = null;
}
// Slashes
if (!ignore_double_slash && (m = re_double_slash.exec(url_str)) !== null) {
// Process auth and host
this.slashes = true;
url_str = url_str.substr(m[0].length);
// Find end
m = re_hierarchy_sep.exec(url_str);
url_part = url_str.substr(0, m.index);
url_str = url_str.substr(m.index);
// Auth
if ((m = re_auth.exec(url_part)) !== null) {
this.auth = m[1];
url_part = url_part.substr(m[0].length);
}
else {
this.auth = null;
}
// Port
if ((m = re_port.exec(url_part)) !== null) {
this.port = parseInt(m[1], 10);
url_part = url_part.substr(0, m.index);
}
else {
this.port = null;
}
// Host name
url_part = url_part.toLowerCase();
this.hostname = url_part;
this.host = url_part;
if (this.port !== null) {
this.host += ":";
this.host += this.port;
}
}
else {
// Null
this.slashes = false;
this.host = null;
this.hostname = null;
this.auth = null;
this.port = null;
}
// Path
m = re_path_sep.exec(url_str);
this.pathname = url_str.substr(0, m.index);
if (this.pathname.length === 0 && this.slashes) {
this.pathname = "/";
}
url_str = url_str.substr(m.index);
// Search
m = re_search_sep.exec(url_str);
this.search = url_str.substr(0, m.index);
url_str = url_str.substr(m.index);
// Fragment
this.hash = url_str;
// Done
return this;
},
resolve: function (to) {
// Other parser
var other = new URLParser(to);
// Protocol
if (other.protocol === null) {
// Copy protocol
other.protocol = this.protocol;
}
// Hierarchy
if (!other.slashes && this.slashes) {
// Copy host
other.slashes = true;
other.host = this.host;
other.hostname = this.hostname;
other.auth = this.auth;
other.port = this.port;
}
// Path
if (other.pathname.length > 0) {
// Join paths
other.pathname = normalize_path(join_paths(this.pathname, other.pathname));
}
else {
// Copy path
other.pathname = this.pathname;
// Search
if (other.search.length === 0) {
// Copy search
other.search = this.search;
// Hash
if (other.hash.length === 0) {
// Copy hash
other.hash = this.hash;
}
}
}
// Done
return other.join();
},
join: function () {
// Form
var url_str = "";
if (this.protocol !== null) {
url_str += this.protocol;
}
if (this.slashes) {
url_str += "//";
if (this.auth !== null) {
url_str += this.auth;
url_str += "@";
}
url_str += this.hostname;
if (this.port !== null) {
url_str += ":";
url_str += this.port;
}
}
url_str += this.pathname;
url_str += this.search;
url_str += this.hash;
return url_str;
},
};
// Functions
return URLParser;
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment