-
-
Save Yaffle/1088850 to your computer and use it in GitHub Desktop.
| /*jslint regexp: true, maxerr: 50, indent: 2 */ | |
| (function (global) { | |
| "use strict"; | |
| function URLUtils(url, baseURL) { | |
| var m = String(url).replace(/^\s+|\s+$/g, "").match(/^([^:\/?#]+:)?(?:\/\/(?:([^:@\/?#]*)(?::([^:@\/?#]*))?@)?(([^:\/?#]*)(?::(\d*))?))?([^?#]*)(\?[^#]*)?(#[\s\S]*)?/); | |
| if (!m) { | |
| throw new RangeError(); | |
| } | |
| var protocol = m[1] || ""; | |
| var username = m[2] || ""; | |
| var password = m[3] || ""; | |
| var host = m[4] || ""; | |
| var hostname = m[5] || ""; | |
| var port = m[6] || ""; | |
| var pathname = m[7] || ""; | |
| var search = m[8] || ""; | |
| var hash = m[9] || ""; | |
| if (baseURL !== undefined) { | |
| var base = new URLUtils(baseURL); | |
| var flag = protocol === "" && host === "" && username === ""; | |
| if (flag && pathname === "" && search === "") { | |
| search = base.search; | |
| } | |
| if (flag && pathname.charAt(0) !== "/") { | |
| pathname = (pathname !== "" ? (((base.host !== "" || base.username !== "") && base.pathname === "" ? "/" : "") + base.pathname.slice(0, base.pathname.lastIndexOf("/") + 1) + pathname) : base.pathname); | |
| } | |
| // dot segments removal | |
| var output = []; | |
| pathname.replace(/^(\.\.?(\/|$))+/, "") | |
| .replace(/\/(\.(\/|$))+/g, "/") | |
| .replace(/\/\.\.$/, "/../") | |
| .replace(/\/?[^\/]*/g, function (p) { | |
| if (p === "/..") { | |
| output.pop(); | |
| } else { | |
| output.push(p); | |
| } | |
| }); | |
| pathname = output.join("").replace(/^\//, pathname.charAt(0) === "/" ? "/" : ""); | |
| if (flag) { | |
| port = base.port; | |
| hostname = base.hostname; | |
| host = base.host; | |
| password = base.password; | |
| username = base.username; | |
| } | |
| if (protocol === "") { | |
| protocol = base.protocol; | |
| } | |
| } | |
| this.origin = protocol + (protocol !== "" || host !== "" ? "//" : "") + host; | |
| this.href = protocol + (protocol !== "" || host !== "" ? "//" : "") + (username !== "" ? username + (password !== "" ? ":" + password : "") + "@" : "") + host + pathname + search + hash; | |
| this.protocol = protocol; | |
| this.username = username; | |
| this.password = password; | |
| this.host = host; | |
| this.hostname = hostname; | |
| this.port = port; | |
| this.pathname = pathname; | |
| this.search = search; | |
| this.hash = hash; | |
| } | |
| global.URLUtils = URLUtils; | |
| }(this)); |
@johan: I don't believe that works in IE, but even more so, crossing the DOM line from JavaScript tends to be slower than a native-JS solution. Requiring a DOM also prevent anyone from creating a cross-environment library.
Forked to fix a small bug: since pathname is guaranteed to start with /, adding an extra / before the leading part of base.pathname is unnecessary.
The following case breaks, as the @ gets detected as the auth part:
https://service.domain.com/[email protected]
Suggested change is:
var m = String(url).replace(/^\s+|\s+$/g, "").match(/^([^:\/?#]+:)?(?:\/\/(?:([^:@]*)(?::([^:@]*))?@)?(([^:\/?#]*)(?::(\d*))?))?([^?#]*)(\?[^#]*)?(#[\s\S]*)?/);to
var m = String(url).replace(/^\s+|\s+$/g, "").match(/^([^:\/?#]+:)?(?:\/\/(?:([^:@\/]*)(?::([^:@\/]*))?@)?(([^:\/?#]*)(?::(\d*))?))?([^?#]*)(\?[^#]*)?(#[\s\S]*)?/);I'm not sure this is 100% correct though - better suggestions welcome.
@guybedford,
Thanks, good catch, I have updated my code.
the specification, written by Anne van Kesteren - https://url.spec.whatwg.org/#authority-state - tells, that "/", "", "?", and "#" should not be in "authority".
@Yaffle thanks so much for the quick response.
When updating to this new code in my tests, I've hit two more issues now unfortunately -
new URLUtils('asdf', 'http://example.org/test')gives an href ofhttp://example.org//asdfinstead ofhttp://example.org/asdfnew URLUtils('asdf', 'file:///example.org/test')gives an href offile:/example.org/asdfinstead offile:///example.org/asdf
These issues didn't happen in the original code that I'm still using though so will stick with that for now.
@guybedford, Thanks again,
I updated the code to fix those issues too. Possibly, there are other issues, I did not test well.
P.S. URLUtils tries to match URL API available in Chrome and Firefox - new URL('asdf', 'file:///example.org/test')
@Yaffle perhaps it is worth considering turning this into a polyfill repo?
@guybedford, there is one - https://github.com/Polymer/URL
A minor performance optimization for line 21 can be useful:
var base = baseURL instanceof URLUtils ? baseURL : new URLUtils(baseURL);
(Unless you're using this in node or similar, where you wouldn't have an url resolver built into the browser DOM already, of course.)