ref: a5214c3c2a9396417edd4658e1344126da856ba9
dir: /domino-lib/URL.js/
"use strict"; module.exports = URL; function URL(url) { if (!url) return Object.create(URL.prototype); // Can't use String.trim() since it defines whitespace differently than HTML this.url = url.replace(/^[ \t\n\r\f]+|[ \t\n\r\f]+$/g, ""); // See http://tools.ietf.org/html/rfc3986#appendix-B // and https://url.spec.whatwg.org/#parsing var match = URL.pattern.exec(this.url); if (match) { if (match[2]) this.scheme = match[2]; if (match[4]) { // parse username/password var userinfo = match[4].match(URL.userinfoPattern); if (userinfo) { this.username = userinfo[1]; this.password = userinfo[3]; match[4] = match[4].substring(userinfo[0].length); } if (match[4].match(URL.portPattern)) { var pos = match[4].lastIndexOf(':'); this.host = match[4].substring(0, pos); this.port = match[4].substring(pos+1); } else { this.host = match[4]; } } if (match[5]) this.path = match[5]; if (match[6]) this.query = match[7]; if (match[8]) this.fragment = match[9]; } } URL.pattern = /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/; URL.userinfoPattern = /^([^@:]*)(:([^@]*))?@/; URL.portPattern = /:\d+$/; URL.authorityPattern = /^[^:\/?#]+:\/\//; URL.hierarchyPattern = /^[^:\/?#]+:\//; // Return a percentEncoded version of s. // S should be a single-character string // XXX: needs to do utf-8 encoding? URL.percentEncode = function percentEncode(s) { var c = s.charCodeAt(0); if (c < 256) return "%" + c.toString(16); else throw Error("can't percent-encode codepoints > 255 yet"); }; URL.prototype = { constructor: URL, // XXX: not sure if this is the precise definition of absolute isAbsolute: function() { return !!this.scheme; }, isAuthorityBased: function() { return URL.authorityPattern.test(this.url); }, isHierarchical: function() { return URL.hierarchyPattern.test(this.url); }, toString: function() { var s = ""; if (this.scheme !== undefined) s += this.scheme + ":"; if (this.isAbsolute()) { s += '//'; if (this.username || this.password) { s += this.username || ''; if (this.password) { s += ':' + this.password; } s += '@'; } if (this.host) { s += this.host; } } if (this.port !== undefined) s += ":" + this.port; if (this.path !== undefined) s += this.path; if (this.query !== undefined) s += "?" + this.query; if (this.fragment !== undefined) s += "#" + this.fragment; return s; }, // See: http://tools.ietf.org/html/rfc3986#section-5.2 // and https://url.spec.whatwg.org/#constructors resolve: function(relative) { var base = this; // The base url we're resolving against var r = new URL(relative); // The relative reference url to resolve var t = new URL(); // The absolute target url we will return if (r.scheme !== undefined) { t.scheme = r.scheme; t.username = r.username; t.password = r.password; t.host = r.host; t.port = r.port; t.path = remove_dot_segments(r.path); t.query = r.query; } else { t.scheme = base.scheme; if (r.host !== undefined) { t.username = r.username; t.password = r.password; t.host = r.host; t.port = r.port; t.path = remove_dot_segments(r.path); t.query = r.query; } else { t.username = base.username; t.password = base.password; t.host = base.host; t.port = base.port; if (!r.path) { // undefined or empty t.path = base.path; if (r.query !== undefined) t.query = r.query; else t.query = base.query; } else { if (r.path.charAt(0) === "/") { t.path = remove_dot_segments(r.path); } else { t.path = merge(base.path, r.path); t.path = remove_dot_segments(t.path); } t.query = r.query; } } } t.fragment = r.fragment; return t.toString(); function merge(basepath, refpath) { if (base.host !== undefined && !base.path) return "/" + refpath; var lastslash = basepath.lastIndexOf("/"); if (lastslash === -1) return refpath; else return basepath.substring(0, lastslash+1) + refpath; } function remove_dot_segments(path) { if (!path) return path; // For "" or undefined var output = ""; while(path.length > 0) { if (path === "." || path === "..") { path = ""; break; } var twochars = path.substring(0,2); var threechars = path.substring(0,3); var fourchars = path.substring(0,4); if (threechars === "../") { path = path.substring(3); } else if (twochars === "./") { path = path.substring(2); } else if (threechars === "/./") { path = "/" + path.substring(3); } else if (twochars === "/." && path.length === 2) { path = "/"; } else if (fourchars === "/../" || (threechars === "/.." && path.length === 3)) { path = "/" + path.substring(4); output = output.replace(/\/?[^\/]*$/, ""); } else { var segment = path.match(/(\/?([^\/]*))/)[0]; output += segment; path = path.substring(segment.length); } } return output; } }, };