I always want to shoot myself in the head when looking at code like the following:
var url = "http://example.org/foo?bar=baz",
separator = url.indexOf('?') > -1 ? '&' : '?';
url += separator + encodeURIComponent("foo") + "=" + encodeURIComponent("bar");
I still can't believe javascript - the f**ing backbone-language of the web - doesn't offer an API for mutating URLs. Browsers (Firefox) don't expose the Location
object (the structure behind window.location). Yes, one could think of decomposed IDL attributes as a native URL management library. But it relies on the DOM element <a>, it's slow and doesn't offer any convenience at all.
How about a nice, clean and simple API for mutating URIs:
var url = new URI("http://example.org/foo?bar=baz");
url.addQuery("foo", "bar");
URI.js is here to help with that.
// mutating URLs
URI("http://example.org/foo.html?hello=world")
.username("rodneyrehm")
// -> http://[email protected]/foo.html?hello=world
.username("")
// -> http://example.org/foo.html?hello=world
.directory("bar")
// -> http://example.org/bar/foo.html?hello=world
.suffix("xml")
// -> http://example.org/bar/foo.xml?hello=world
.query("")
// -> http://example.org/bar/foo.xml
.tld("com")
// -> http://example.com/bar/foo.xml
.query({ foo: "bar", hello: ["world", "mars"] });
// -> http://example.com/bar/foo.xml?foo=bar&hello=world&hello=mars
// cleaning things up
URI("?&foo=bar&&foo=bar&foo=baz&")
.normalizeQuery();
// -> ?foo=bar&foo=baz
// working with relative paths
URI("/foo/bar/baz.html")
.relativeTo("/foo/bar/world.html");
// -> ./baz.html
URI("/foo/bar/baz.html")
.relativeTo("/foo/bar/sub/world.html")
// -> ../baz.html
.absoluteTo("/foo/bar/sub/world.html");
// -> /foo/bar/baz.html
// URI Templates
URI.expand("/foo/{dir}/{file}", {
dir: "bar",
file: "world.html"
});
// -> /foo/bar/world.html
See the About Page and API Docs for more stuff.
I guess you'll manage to use the build tool or follow the instructions below to combine and minify the various files into URI.min.js - and I'm fairly certain you know how to <script src=".../URI.min.js"></script>
that sucker, too.
Install with npm install URIjs
or add "URIjs"
to the dependencies in your package.json
.
// load URI.js
var URI = require('URIjs');
// load an optional module (e.g. URITemplate)
var URITemplate = require('URIjs/src/URITemplate');
URI("/foo/bar/baz.html")
.relativeTo("/foo/bar/sub/world.html")
// -> ../baz.html
Clone the URI.js repository or use a package manager to get URI.js into your project.
require.config({
paths: {
URIjs: 'where-you-put-uri.js/src'
}
});
require(['URIjs/URI'], function(URI) {
console.log("URI.js and dependencies: ", URI("//amazon.co.uk").is('sld') ? 'loaded' : 'failed');
});
require(['URIjs/URITemplate'], function(URITemplate) {
console.log("URITemplate.js and dependencies: ", URITemplate._cache ? 'loaded' : 'failed');
});
See the build tool or use Google Closure Compiler:
// ==ClosureCompiler==
// @compilation_level SIMPLE_OPTIMIZATIONS
// @output_file_name URI.min.js
// @code_url http://medialize.github.com/URI.js/src/IPv6.js
// @code_url http://medialize.github.com/URI.js/src/punycode.js
// @code_url http://medialize.github.com/URI.js/src/SecondLevelDomains.js
// @code_url http://medialize.github.com/URI.js/src/URI.js
// @code_url http://medialize.github.com/URI.js/src/URITemplate.js
// ==/ClosureCompiler==
Documents specifying how URLs work:
- URL - Living Standard
- RFC 3986 - Uniform Resource Identifier (URI): Generic Syntax
- RFC 3987 - Internationalized Resource Identifiers (IRI)
- Punycode: A Bootstring encoding of Unicode for Internationalized Domain Names in Applications (IDNA)
- application/x-www-form-urlencoded (Query String Parameters) and application/x-www-form-urlencoded encoding algorithm
Informal stuff
How other environments do things
If you don't like URI.js, you may like one of these:
- The simple URL Mutation "Hack" (jsPerf comparison)
- URL.js
- furl (Python)
- mediawiki Uri (needs mw and jQuery)
- jurlp
- jsUri
- The simple URL Mutation "Hack" (jsPerf comparison)
- URI Parser
- jQuery-URL-Parser
- Google Closure Uri
- URI.js by Gary Court
- uri-templates by Marc Portier
- URI Template JS by Franz Antesberger
- Temple by Brett Stimmerman
- (jsperf comparison)
- TLD.js - second level domain names
- Public Suffic - second level domain names
- punycode.js - Mathias Bynens
- IPv6.js - Rich Brown - (rewrite of the original)
URI.js is published under the MIT license and GPL v3.
- fixing
relativeTo()
- (Issue #75) - fixing
normalizePath()
to not prepend ./ to relative paths - (Issue #76)
- adding
absoluteTo()
to properly resolve relative scheme - (Issue #71)
- adding
hasQuery()
- (Issue #71) - fixing URI property detection to return 'src' if none was detected (
jquery.URI.js
) - (Issue #69)
- fixing IE9 compatibility with location import:
URI(location)
- fixing string character access for IE7 - (Issue #67), (Issue #68)
- adding
.setQuery()
- (Issue #64) - adding callback argument to
.query()
- adding jQuery 1.9.1 to the test suite
- adding
.fragmentPrefix()
to configure prefix of fragmentURI and fragmentQuery extensions - (Issue #55) - adding docs for
.toString()
,.valueOf()
and.href()
- (Issue #56) - fixing
.relativeTo()
for descendants - (Issue #57)
- fixing build() to properly omit empty query and fragment (Issue #53)
- adding
.resource()
as compound of [path, query, fragment] - adding jQuery 1.8.x compatibility for jQuery.URI.js (remaining backwards compatibility!)
- adding default ports for gopher, ws, wss
- adding
.duplicateQueryParameters()
to control ifkey=value
duplicates have to be preserved or reduced (Issue #51) - updating Punycode.js to version 1.1.1
- improving AMD/Node using UMD returnExports - (Issue #44, Issue #47)
- fixing
.addQuery("empty")
to properly add?empty
- (Issue #46) - fixing parsing of badly formatted userinfo
http://username:pass:word@hostname
- fixing parsing of Windows-Drive-Letter paths
file://C:/WINDOWS/foo.txt
- fixing
URI(location)
to properly parse the URL - (Issue #52) - fixing type error for fragment abuse demos - (Issue #50)
- adding documentation for various encode/decode functions
- adding some pointers on possible problems with URLs to About URIs
- adding tests for fragment abuse and splitting tests into separate scopes
- adding meta-data for Jam and Bower
Note: QUnit seems to be having some difficulties on IE8. While the jQuery-plugin tests fail, the plugin itself works. We're still trying to figure out what's making QUnit "lose its config state".
- fixing parsing of
/wiki/Help:IPA
- (Issue #49)
- fixing
strictEncodeURIComponent()
to properly encode*
to%2A
- fixing IE9's incorrect report of
img.href
being available - (Issue #48)
- fixing SLD detection in
.tld()
-foot.se
would detectt.se
- (Issue #42) - fixing
.absoluteTo()
to comply with RFC 3986 Section 5.2.2 - (Issue #41) - fixing
location
not being available in non-browser environments like node.js (Issue #45 grimen)
- fixing
.segment()
's append operation - (Issue #39)
- fixing URI() constructor passing of
base
- (Issue #33 LarryBattle) - adding
.segment()
accessor - (Issue #34) - upgrading
URI.encode()
to strict URI encoding according to RFC3986 - adding
URI.encodeReserved()
to exclude reserved characters (according to RFC3986) from being encoded - adding URI Template (RFC 6570) support with
URITemplate()
- fixing
.absoluteTo()
to join two relative paths properly - (Issue #29) - adding
.clone()
to copy an URI instance
.directory()
now returns empty string if there is no directory- fixing
.absoluteTo()
to join two relative paths properly - (Issue #29)
- adding URN (
javascript:
,mailto:
, ...) support - adding
.scheme()
as alias of.protocol()
- adding
.userinfo()
to comply with terminology of RFC 3986 - adding jQuery Plugin
src/jquery.URI.js
- fixing relative scheme URLs - (Issue #19 byroot)
- adding Second Level Domain (SLD) Support - (Issue #17)
- fixing global scope leakage - (Issue #15 mark-rushakoff)
- adding CommonJS compatibility - (Issue #11, Evangenieur)
- adding
URI.iso8859()
andURI.unicode()
to switch base charsets - (Issue #10, mortenn) - adding
.iso8859()
and.unicode()
to convert an URI's escape encoding
- updating Punycode.js to version 0.3.0
- adding edge-case tests ("jim")
- fixing edge-cases in .protocol(), .port(), .subdomain(), .domain(), .tld(), .filename()
- fixing parsing of hostname in
.hostname()
- adding
.subdomain()
convenience accessor - improving internal deferred build handling
- fixing thrown Error for
URI("http://example.org").query(true)
- (Issue #6) - adding examples for extending URI.js for fragment abuse, see src/URI.fragmentQuery.js and src/URI.fragmentURI.js - (Issue #2)
- adding
.equals()
for URL comparison - fixing encoding/decoding for
.pathname()
,.directory()
,.filename()
and.suffix()
according to RFC 3986 3.3 - fixing escape spaces in query strings with
+
according to application/x-www-form-urlencoded - fixing to allow
URI.buildQuery()
to build duplicate key=value combinations - fixing
URI(string, string)
constructor to conform with the specification - adding
.readable()
for humanly readable representation of encoded URIs - fixing bug where @ in pathname would be parsed as part of the authority
- adding
URI.withinString()
- adding
.normalizeProtocol()
to lowercase protocols - fixing
.normalizeHostname()
to lowercase hostnames - fixing String.substr() to be replaced by String.substring() - (Issue #1)
- fixing parsing "?foo" to
{foo: null}
Algorithm for collecting URL parameters - fixing building
{foo: null, bar: ""}
to "?foo&bar=" Algorithm for serializing URL parameters - fixing RegExp escaping
- Initial URI.js