+
+
+
+
+
Understanding URIs
+
+
+ Uniform Resource Identifiers (URI) can be one of two things, a Uniform Resource Locator (URL) or a Uniform Resource Name (URN).
+ You likely deal with URLs most of the time. See RFC 3986 for a proper definition of the terms URI, URL and URN
+
+
+
+ URNs name a resource.
+ They are (supposed to) designate a globally unique, permanent identifier for that resource.
+ For example, the URN urn:isbn:0201896834 uniquely identifies Volume 1 of Donald Knuth's The Art of Computer Porgramming.
+ Even if that book goes out of print, that URN will continue to identify that particular book in that particular printing.
+ While the term "URN" technically refers to a specific URI scheme laid out by RFC 2141,
+ the previously-mentioned RFC 3986 indicates that in common usage "URN" refers to any kind of URI that identifies a resource.
+
+
+
+ URLs locate a resource.
+ They designate a protocol to use when looking up the resource and provide an "address" for finding the resource within that scheme.
+ For example, the URL http://tools.ietf.org/html/rfc3986 tells the consumer (most likely a web browser)
+ to use the HTTP protocol to access whatever site is found at the /html/rfc3986 path of tools.ietf.org.
+ URLs are not permanent; it is possible that in the future that the IETF will move to a different domain or even that some other organization will acquire the rights to tools.ietf.org.
+ It is also possible that multiple URLs may locate the same resource;
+ for example, an admin at the IETF might be able to access the document found at the example URL via the ftp:// protocol.
+
+
+
URLs and URNs in URI.js
+
+
+ The distinction between URLs and URNs is one of semantics.
+ In principle, it is impossible to tell, on a purely syntactical level, whether a given URI is a URN or a URL without knowing more about its scheme.
+ Practically speaking, however, URIs that look like HTTP URLs (scheme is followed by a colon and two slashes, URI has an authority component, and paths are delimited by slashes) tend to be URLs,
+ and URIs that look like RFC 2141 URNs (scheme is followed by a colon, no authority component, and paths are delimited by colons) tend to be URNs (in the broad sense of "URIs that name").
+
+
+
+ So, for the purposes of URI.js, the distinction between URLs and URNs is treated as one of syntax.
+ The main functional differences between the two are that (1) URNs will not have an authority element and
+ (2) when breaking the path of the URI into segments, the colon will be used as the delimiter rather than the slash.
+ The most surprising result of this is that mailto: URLs will be considered by URI.js to be URNs rather than URLs.
+ That said, the functional differences will not adversely impact the handling of those URLs.
+
+
+
Components of an URI
+
RFC 3986 Section 3 visualizes the structure of URIs as follows:
+
+URL: foo://example.com:8042/over/there?name=ferret#nose
+ \_/ \______________/\_________/ \_________/ \__/
+ | | | | |
+ scheme authority path query fragment
+ | _____________________|__
+ / \ / \
+URN: urn:example:animal:ferret:nose
+
+
Components of an URL in URI.js
+
+
+ origin
+ __________|__________
+ / \
+ authority
+ | __________|_________
+ | / \
+ userinfo host resource
+ | __|___ ___|___ __________|___________
+ | / \ / \ / \
+ username password hostname port path & segment query fragment
+ | __|___ __|__ ______|______ | __________|_________ ____|____ |
+ | / \ / \ / \ / \ / \ / \ / \
+ foo://username:password@www.example.com:123/hello/world/there.html?name=ferret#foo
+ \_/ \ / \ \ / \__________/ \ \__/
+ | | \ | | \ |
+ scheme subdomain \ tld directory \ suffix
+ \____/ \___/
+ | |
+ domain filename
+
+
+
+
+ In Javascript the query is often referred to as the search.
+ URI.js provides both accessors with the subtle difference of .search() beginning with the ?-character
+ and .query() not.
+
+
+ In Javascript the fragment is often referred to as the hash.
+ URI.js provides both accessors with the subtle difference of .hash() beginning with the #-character
+ and .fragment() not.
+
+
+
Components of an URN in URI.js
+
+
+ urn:example:animal:ferret:nose?name=ferret#foo
+ \ / \________________________/ \_________/ \ /
+ | | | |
+ scheme path & segment query fragment
+
+
+
While RFC 2141 does not define URNs having a query or fragment component, URI.js enables these accessors for convenience.
+
+
URLs - Man Made Problems
+
+
URLs (URIs, whatever) aren't easy. There are a couple of issues that make this simple text representation of a resource a real pain
+
+ - Look simple but have tricky encoding issues
+ - Domains aren't part of the specification
+ - Query String Format isn't part of the specification
+ - Environments (PHP, JS, Ruby, …) handle Query Strings quite differently
+
+
+
Parsing (seemingly) invalid URLs
+
Because URLs look very simple, most people haven't read the formal specification. As a result, most people get URLs wrong on many different levels. The one thing most everybody screws up is proper encoding/escaping.
+
http://username:pass:word@example.org/ is such a case. Often times homebrew URL handling misses escaping the less frequently used parts such as the userinfo.
+
http://example.org/@foo that "@" doesn't have to be escaped according to RFC3986. Homebrew URL handlers often just treat everything between "://" and "@" as the userinfo.
+
some/path/:foo is a valid relative path (as URIs don't have to contain scheme and authority). Since homebrew URL handlers usually just look for the first occurence of ":" to delimit the scheme, they'll screw this up as well.
+
+ is the proper escape-sequence for a space-character within the query string component, while every other component prefers %20. This is due to the fact that the actual format used within the query string component is not defined in RFC 3986, but in the HTML spec.
+
There is encoding and strict encoding - and Javascript won't get them right: encodeURIComponent()
+
+
Top Level Domains
+
The hostname component can be one of may things. An IPv4 or IPv6 address, an IDN or Punycode domain name, or a regular domain name. While the format (and meaning) of IPv4 and IPv6 addresses is defined in RFC 3986, the meaning of domain names is not.
+
DNS is the base of translating domain names to IP addresses. DNS itself only specifies syntax, not semantics. The missing semantics is what's driving us crazy here.
+
ICANN provides a list of registered Top Level Domains (TLD). There are country code TLDs (ccTLDs, assigned to each country, like ".uk" for United Kindom) and generic TLDs (gTLDs, like ".xxx" for you know what). Also note that a TLD may be non-ASCII .香港 (IDN version of HK, Hong Kong).
+
IDN TLDs such as .香港 and the fact that any possible new TLD could pop up next month has lead to a lot of URL/Domain verification tools to fail.
+
+
Second Level Domains
+
To make Things worse, people thought it to be a good idea to introduce Second Level Domains (SLD, ".co.uk" - the commercial namespace of United Kingdom). These SLDs are not up to ICANN to define, they're handled individually by each NIC (Network Information Center, the orgianisation responsible for a specific TLD).
+
Since there is no central oversight, things got really messy in this particular space. Germany doesn't do SDLs, Australia does. Australia has different SLDs than the United Kingdom (".csiro.au" but no ".csiro.uk"). The individual NICs are not required to publish their arbitrarily chosen SLDs in a defined syntax anywhere.
+
You can scour each NIC's website to find some hints at their SLDs. You can look them up on Wikipedia and hope they're right. Or you can use PublicSuffix.
+
Speaking of PublicSuffix, it's time mentioning that browser vendors actually keep a list of known Second Level Domains. They need to know those for security issues. Remember cookies? They can be read and set on a domain level. What do you think would happen if "co.uk" was treated as the domain? amazon.co.uk would be able to read the cookies of google.co.uk. PublicSuffix also contains custom SLDs, such as .dyndns.org. While this makes perfect sense for browser security, it's not what we need for basic URL handling.
+
TL;DR: It's a mess.
+
+
The Query String
+
PHP (parse_str()) will automatically parse the query string and populate the superglobal $_GET for you. ?foo=1&foo=2 becomes $_GET = array('foo' => 2);, while ?foo[]=1&foo[]=2 becomes $_GET = array('foo' => array("1", "2"));.
+
Ruby's CGI.parse() turns ?a=1&a=2 into {"a" : ["1", "2"]}, while Ruby on Rails chose the PHP-way.
+
Python's parse_qs() doesn't care for [] either.
+
Most other languages don't follow the []-style array-notation and deal with this mess differently.
+
TL;DR: You need to know the target-environment, to know how complex query string data has to be encoded
+
+
The Fragment
+
Given the URL http://example.org/index.html#foobar, browsers only request http://example.org/index.html, the fragment #foobar is a client-side thing.
+
+
+
+
diff --git a/mayan/apps/appearance/static/appearance/vendors/URI.js-1.19.1/bower.json b/mayan/apps/appearance/static/appearance/vendors/URI.js-1.19.1/bower.json
new file mode 100644
index 0000000000..a043b31a86
--- /dev/null
+++ b/mayan/apps/appearance/static/appearance/vendors/URI.js-1.19.1/bower.json
@@ -0,0 +1,16 @@
+{
+ "name": "urijs",
+ "version": "1.19.1",
+ "main": "src/URI.js",
+ "ignore": [
+ ".*",
+ "*.css",
+ "/*.js",
+ "/*.html",
+ "/*.json",
+ "utils",
+ "test",
+ "prettify"
+ ],
+ "license": "MIT"
+}
diff --git a/mayan/apps/appearance/static/appearance/vendors/URI.js-1.19.1/build.html b/mayan/apps/appearance/static/appearance/vendors/URI.js-1.19.1/build.html
new file mode 100644
index 0000000000..e36b47a65a
--- /dev/null
+++ b/mayan/apps/appearance/static/appearance/vendors/URI.js-1.19.1/build.html
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+ -
+ Constructing an URI
+
+
+ -
+ Working with URI parts
+
+
+ -
+ Working with the query string
+
+
+ -
+ Working with the Fragment (Hash)
+
+
+ -
+ Normalizing URLs
+
+
+ -
+ Charsets / Encodings
+
+
+ - readable()
+ -
+ Relative and Absolute URLs
+
+
+ -
+ Comparing URLs
+
+
+ -
+ Parsing URLs
+
+
+ -
+ Serializing URLs
+
+
+ -
+ Encoding and Decoding URLs
+
+
+ -
+ Static Helper Functions
+
+
+
+
+
URI.js API
+
+
URI Constructor
+
var uri = new URI(); // same as new URI(location.href)
+// string
+var uri = new URI("http://example.org");
+
+// URI object for cloning
+var uri = new URI(new URI("http://example.org"));
+
+// URI parts object
+var uri = new URI({
+ protocol: "http",
+ hostname: "example.org"
+});
+
+// without new keyword
+var uri = URI("http://example.org");
+
+// resolving right in the constructor
+var uri = URI("../foobar.html", "http://example.org/hello/world.html");
+// which is exactly the same as
+URI("../foobar.html").absoluteTo("http://example.org/hello/world.html");
+// but specified in URL constructor
+
+
The following parts can be specified in an object:
+
var uri = new URI({
+ protocol: "http", // no trailing :
+ username: "user",
+ password: "pass",
+ hostname: "example.org",
+ port: "80", // string, please
+ // "path", not "pathname", sorry
+ path: "/foo/bar.html",
+ // "query", not "search", sorry
+ query: "foo=bar&bar=baz", // no leading ?
+ // "fragment", not "hash", sorry
+ fragment: "frag" // no leading #
+});
+
+
using only components of URIs:
+
// Look ma! I'm only working the pathname
+var uri = new URI("some/directory/file.html");
+
+// Look ma! I'm only working the query string
+var uri = new URI("?foo=bar");
+
+// Look ma! I'm only working the fragment / hash
+var uri = new URI("#call-me-hash");
+
+// and any combination of the above…
+
+
using DOM elements:
+
var element = document.createElement('a');
+element.href = 'http://example.org';
+var uri = new URI(element);
+// uri.domain() === 'example.org';
+
The following DOM elements can be parsed:
+
+<a href="...">
+<blockquote cite="...">
+<link href="...">
+<base href="...">
+<script src="...">
+<form action="...">
+<input type="image" src="...">
+<img src="...">
+<area href="...">
+<iframe src="...">
+<embed src="...">
+<source src="...">
+<track src="...">
+
+
+any other element yields URI("")
+
+
+
cloning URIs
+
Get a copy of the current URI instance
+
var uri = new URI("http://example.org");
+var uri2 = uri.clone();
+
+uri2.tld("com");
+uri == "http://example.org/";
+uri2 == "http://example.com/";
+
+
href()
+
get and set the entire URI
+
var uri = URI("http://example.com");
+uri.href() === "http://example.com/";
+
+uri.href("ftp://google.org");
+uri.toString() === "ftp://google.org/"
+
+
toString(), valueOf()
+
serialize the URI to string. valueOf() is an alias to toString(), as string is the base primitive.
+
var uri = URI("http://example.com");
+var s = uri.toString();
+typeof s === "string";
+s === "http://example.com/";
+
+
+
protocol(), scheme()
+
.scheme() is an alias of .protocol()
+
var uri = new URI("http://example.org/foo/hello.html");
+// get protocol
+uri.protocol(); // returns string "http"
+// set protocol
+uri.protocol("ftp"); // returns the URI instance for chaining
+
+// relative scheme
+uri.protocol("");
+uri.toString() === "//example.org/foo/hello.html";
+
Throws a TypeError on illegal input, that is anything but [a-z0-9.+-] and [empty string] and null
+
+
username()
+
var uri = new URI("http://user:pass@example.org/foo/hello.html");
+// get username
+uri.username(); // returns string "user"
+// set username
+uri.username("user"); // returns the URI instance for chaining
+
+
password()
+
var uri = new URI("http://user:pass@example.org/foo/hello.html");
+// get password
+uri.password(); // returns string "pass"
+// set password
+uri.password("user"); // returns the URI instance for chaining
+
+
hostname()
+
var uri = new URI("http://example.org/foo/hello.html");
+// get hostname
+uri.hostname(); // returns string
+// set hostname
+uri.hostname("example.org"); // returns the URI instance for chaining
+
.hostname() returns the actual hostname, whereas .host() returns the hostname including the port
+
+
port()
+
var uri = new URI("http://example.org:8080/foo/hello.html");
+// get port
+uri.port(); // returns string "8080"
+// set port
+uri.port("80"); // returns the URI instance for chaining
+
although the port may be considered an integer, within URI it is a string.
+
Throws a TypeError on illegal input
+
+
host()
+
var uri = new URI("http://example.org:80/foo/hello.html");
+// get host
+uri.host(); // returns string "example.org:80"
+// set host
+uri.host("example.org:80"); // returns the URI instance for chaining
+
.hostname() returns the actual hostname, whereas .host() returns the hostname including the port
+
Throws a TypeError if path is part of the input
+
+
userinfo()
+
Userinfo is comprised of username and password
+
var uri = new URI("http://user:pass@example.org:88/foo/hello.html");
+// get userinfo
+uri.userinfo(); // returns string "user:pass"
+// set userinfo
+uri.userinfo("user:pass"); // returns the URI instance for chaining
+
+
authority()
+
Authority is comprised of username, password, hostname and port
+
var uri = new URI("http://user:pass@example.org:88/foo/hello.html");
+// get authority
+uri.authority(); // returns string "user:pass@example.org:88"
+// set authority
+uri.authority("user:pass@example.org:80"); // returns the URI instance for chaining
+
.authority() will reset any of username, password and port if they're not specified.
+
Throws a TypeError if path is part of the input
+
+
origin()
+
Origin is comprised of the scheme and authority.
+
var uri = new URI("http://example.com/foo.html?q=hello");
+// get origin
+uri.origin(); // returns string "http://example.com"
+// set origin
+uri.origin('https://other.org'); // returns URI instance for chaining
+
+// the URI will now have the string representation of:
+// "https://other.org/foo.html?q=hello"
+
.origin() will reset the entire authority, including username, password and port if not specified in the new origin.
+
.origin() will be empty if there is no authority.
+
.origin() will be the same as .authority() (e.g. "example.org") if there is no scheme available.
+
+
domain()
+
.domain() is a convenience method that returns example.org from the hostname www.example.org.
+
var uri = new URI("http://example.org/foo/hello.html");
+// get domain
+uri.domain(); // returns string "example.org"
+// set domain
+uri.domain("otherdomain.com"); // returns the URI instance for chaining
+
+// Second Level Domain (SLD) Support (as of URI.js 1.5.0)
+uri = new URI("http://example.co.uk/foo/hello.html");
+uri.domain(); // return string "example.co.uk"
+uri.domain(true); // return string "co.uk"
+
.domain() will throw an error if you pass it an empty string.
+
Throws a TypeError on illegal input
+
+
subdomain()
+
.subdomain() is a convenience method that returns www from the hostname www.example.org.
+
var uri = new URI("http://www.example.org/foo/hello.html");
+// get subdomain
+uri.subdomain(); // returns string "www"
+// set subdomain
+uri.subdomain("other.subdomain"); // returns the URI instance for chaining
+
Throws a TypeError on illegal input
+
+
tld()
+
.tld() is a convenience method that returns org from the hostname www.example.org.
+
var uri = new URI("http://example.org/foo/hello.html");
+// get tld
+uri.tld(); // returns string "org"
+// set tld
+uri.tld("com"); // returns the URI instance for chaining
+
+// Second Level Domain (SLD) Support (as of URI.js 1.5.0)
+uri = new URI("http://example.co.uk/foo/hello.html");
+uri.tld(); // return string "co.uk"
+uri.tld(true); // return string "uk"
+
Throws an Error if you pass it an empty string or use it on an IP-host.
+
+
pathname(), path()
+
.path() is an alias of .pathname()
+
var uri = new URI("http://example.org/foo/hello.html");
+// get pathname
+uri.pathname(); // returns string "/foo/hello.html"
+// set pathname
+uri.pathname("/foo/hello.html"); // returns the URI instance for chaining
+
+// will encode for you
+uri.pathname("/hello world/");
+uri.pathname() === "/hello%20world/";
+// will decode for you
+uri.pathname(true) === "/hello world/";
+
+// will return empty string for empty paths, but:
+URI("").path() === "";
+URI("/").path() === "/";
+URI("http://example.org").path() === "/";
+
+
+
directory()
+
.directory() is an convenience method for mutating the directory part of a path
+
var uri = new URI("http://example.org/foo/hello.html");
+// get directory
+uri.directory(); // returns string "/foo" (no trailing slash)
+// set directory
+uri.directory("/bar"); // returns the URI instance for chaining
+// uri == "http://example.org/bar/hello.html"
+
+// will encode for you
+uri.directory("/hello world/");
+uri.directory() === "/hello%20world";
+// will decode for you
+uri.directory(true) === "/hello world";
+
+uri.href("http://example.com/foo").directory()
+// -&t; "/"
+uri.href("/foo").directory()
+// -&t; "/"
+uri.href("foo").directory()
+// -&t; ""
+
+
filename()
+
.filename() is an convenience method for mutating the filename part of a path
+
var uri = new URI("http://example.org/foo/hello.html");
+// get filename
+uri.filename(); // returns string "hello.html" (no leading slash)
+// set filename
+uri.filename("world.xml"); // returns the URI instance for chaining
+// uri == "http://example.org/foo/world.xml"
+
+// will encode for you
+uri.filename("hello world.html");
+uri.filename() === "hello%20world.html";
+// will decode for you
+uri.filename(true) === "hello world.html";
+
If you pass ../file.html, the directory will be changed accordingly
+
+
suffix()
+
.suffix() is an convenience method for mutating the filename part of a path
+
var uri = new URI("http://example.org/foo/hello.html");
+// get suffix
+uri.suffix(); // returns string "html" (no leading dot)
+// set suffix
+uri.suffix("xml"); // returns the URI instance for chaining
+// uri == "http://example.org/bar/world.xml"
+
+// will encode for you
+uri.suffix("würgh");
+uri.suffix() === "w%C3%BCrgh";
+// will decode for you
+uri.suffix(true) === "würgh";
+
+
segment()
+
.segment() allows convenient access to directory levels / URN segments within the path. See .segmentCoded() for an interface that transparently encodes and decodes path segments.
+
var uri = new URI("http://example.org/foo/hello.html");
+// get segments
+uri.segment(); // returns array ["foo", "hello.html"]
+// set segments
+uri.segment(["foo", "bar", "foobar.html"]); // -> http://example.org/foo/bar/foobar.html
+
+// get specific level
+uri.segment(0); // returns "foo"
+uri.segment(1); // returns "bar"
+uri.segment(-1); // returns "foobar.html"
+// set specific level
+uri.segment(0, "bar"); // -> http://example.org/bar/bar/foobar.html
+// remove specific level
+uri.segment(0, ""); // -> http://example.org/bar/foobar.html
+
+// append level
+uri.segment("appendthis"); // -> http://example.org/bar/foobar.html/appendthis
+
+
segmentCoded()
+
.segmentCoded() works the same way .segment() does, with the difference of transparently encoding and decoding values.
+
var uri = new URI("http://example.org/foo/hello%20world.html");
+// get segments
+uri.segmentCoded(); // returns array ["foo", "hello world.html"]
+// set segments
+uri.segmentCoded(["foo", "bar", "foo bar.html"]); // -> http://example.org/foo/bar/foo%20bar.html
+
+// get specific level
+uri.segmentCoded(0); // returns "foo"
+uri.segmentCoded(1); // returns "bar"
+uri.segmentCoded(-1); // returns "foo bar.html"
+// set specific level
+uri.segmentCoded(0, "bar bam"); // -> http://example.org/bar%20bam/bar/foobar.html
+// remove specific level
+uri.segmentCoded(0, ""); // -> http://example.org/bar/foobar.html
+
+// append level
+uri.segmentCoded("append this"); // -> http://example.org/bar/foobar.html/append%20this
+
+
+
search(), query()
+
var uri = new URI("http://example.org/foo/hello.html?foo=bar&bar=baz");
+// get search
+uri.search(); // returns string "?foo=bar&bar=baz" (leading ?)
+// get query
+uri.query(); // returns string "foo=bar&bar=baz" (no leading ?)
+
+// .query() and .search() behave the same for the following:
+
+// set search
+uri.search("?foo=bar&bar=baz"); // returns the URI instance for chaining
+uri.search("foo=bar&bar=baz"); // returns the URI instance for chaining
+// uri == "http://example.org/bar/world.html?foo=bar&bar=baz"
+
+// remove query
+uri.search(""); // returns the URI instance for chaining
+// uri == "http://example.org/bar/world.html"
+
+// get data map:
+uri.search(true); // returns { foo: "bar", hello : ["world", "mars"] }
+
+// set data map:
+uri.search({ foo: "bar", hello : ["world", "mars"] });
+// uri == "http://example.org/bar/world.html?foo=bar&hello=world&hello=mars"
+
+// overwrite data through callback
+uri.search(function(data) {
+ return { hello : "world" };
+});
+// uri == "http://example.org/bar/world.html?hello=world"
+
+// augment data through callback
+uri.search(function(data) {
+ data.foo = "bar";
+});
+// uri == "http://example.org/bar/world.html?hello=world&foo=bar"
+
+// CAUTION: beware of arrays, the following are not quite the same
+// If you're dealing with PHP, you probably want the latter…
+uri.search("?foo=bar&bar=baz");
+uri.search("?foo=bar[]&bar[]=baz");
+
Note that names and values passed in an object are encoded automatically.
+ The object, resulting from parsing the query string, contains decoded values
+
Hint: If you're using jQuery, have a look at their .serialize() function.
+
+
hash(), fragment()
+
var uri = new URI("http://example.org/foo/hello.html#world");
+// get hash
+uri.hash(); // returns string "#world" (leading #)
+// get fragment
+uri.fragment(); // returns string "world" (no leading #)
+
+// remove fragment
+uri.fragment(""); // returns the URI instance for chaining
+// uri == "http://example.org/bar/world.html"
+
+// .hash() and .fragment() behave the same for the following:
+
+// set hash
+uri.hash("#mars"); // returns the URI instance for chaining
+uri.hash("mars"); // returns the URI instance for chaining
+// uri == "http://example.org/bar/world.xml#mars"
+
+
resource()
+
Resource is comprised of path, query and fragment
+
var uri = new URI("http://example.org/foo/hello.html?query=string#hash");
+// get resource
+uri.resource(); // returns string "/foo/hello.html?query=string#hash"
+// set resource
+uri.resource("/mars.txt?query=foo#other"); // returns the URI instance for chaining
+
.resource() will reset any of path, query and fragment if they're not specified.
+
+
is()
+
.is() tells what a URL is. It responds with a boolean and can be asked the following questions:
+
+ relativetrue if URL doesn't have a hostname
+ absolutetrue if URL has a hostname
+ urntrue if URI looks like a URN
+ urltrue if URI is a URL
+ domain, nametrue if hostname is not an IP
+ sldtrue if hostname is a second level domain (i.e. "example.co.uk")
+ idntrue if hostname contains non-alphanumeric characters and is not an IP
+ punycodetrue if hostname contains xn--
+ iptrue if hostname is IPv4 or IPv6
+ ip4, ipv4, inet4true if hostname is IPv4
+ ip6, ipv6, inet6true if hostname is IPv6
+
+
var uri = new URI("http://example.org/");
+uri.is("relative") === false;
+uri.is("absolute") === true;
+uri.is("urn") === false;
+uri.is("url") === true;
+uri.is("name") === true;
+uri.is("sld") === false;
+uri.is("punycode") === false;
+uri.is("IDN") === false; // case doesn't matter
+uri.is("idn") === false; // case doesn't matter
+uri.is("ip") === false;
+
+var uri = new URI("http://123.123.123.123/");
+uri.is("relative") === false;
+uri.is("absolute") === true;
+uri.is("urn") === false;
+uri.is("url") === true;
+uri.is("name") === false;
+uri.is("sld") === false;
+uri.is("IP") === true;
+uri.is("IPv4") === true;
+uri.is("IPv6") === false;
+
+var uri = new URI("http://fe80:0000:0000:0000:0204:61ff:fe9d:f156/");
+uri.is("IP") === true;
+uri.is("IPv4") === false;
+uri.is("IPv6") === true;
+
+var uri = new URI("/hello/world.html");
+uri.is("relative") === true;
+uri.is("absolute") === false;
+uri.is("urn") === false;
+uri.is("url") === true;
+uri.is("name") === false;
+uri.is("IP") === false;
+
+var uri = new URI("http://example.co.uk/");
+uri.is("name") === true;
+uri.is("sld") === true;
+
+var uri = new URI("mailto:mail@example.org");
+uri.is("relative") === false;
+uri.is("absolute") === false;
+uri.is("urn") === true;
+uri.is("url") === false;
+uri.is("name") === false;
+uri.is("sld") === false;
+uri.is("punycode") === false;
+uri.is("idn") === false;
+uri.is("ip") === false;
+
+
+
Working with the query string
+
+
setSearch(), setQuery()
+
.setQuery() is an alias of .setSearch()
+
var uri = new URI("?hello=world");
+uri.setSearch("hello", "mars"); // returns the URI instance for chaining
+// uri == "?hello=mars"
+
+uri.setSearch({ foo: "bar", goodbye : ["world", "mars"] });
+// uri == "?hello=mars&foo=bar&goodbye=world&goodbye=mars"
+
+uri.setSearch("goodbye", "sun");
+// uri == "?hello=mars&foo=bar&goodbye=sun"
+
+// CAUTION: beware of arrays, the following are not quite the same
+// If you're dealing with PHP, you probably want the latter…
+uri.setSearch("foo", ["bar", "baz"]);
+uri.setSearch("foo[]", ["bar", "baz"]);
+
Note that names and values passed in are encoded automatically.
+
+
addSearch(), addQuery()
+
.addQuery() is an alias of .addSearch()
+
var uri = new URI("?hello=world");
+uri.addSearch("hello", "mars"); // returns the URI instance for chaining
+// uri == "?hello=world&hello=mars"
+
+uri.addSearch({ foo: "bar", goodbye : ["world", "mars"] });
+// uri == "?hello=world&hello=mars&foo=bar&goodbye=world&goodbye=mars"
+
+uri.addSearch("no-value");
+// uri == "?hello=world&hello=mars&foo=bar&goodbye=world&goodbye=mars&no-value"
+
+// CAUTION: beware of arrays, the following are not quite the same
+// If you're dealing with PHP, you probably want the latter…
+uri.addSearch("foo", ["bar", "baz"]);
+uri.addSearch("foo[]", ["bar", "baz"]);
+
Note that names and values passed in are encoded automatically.
+
+
removeSearch(), removeQuery()
+
.removeQuery() is an alias of .removeSearch()
+
var uri = new URI("?hello=world&hello=mars&foo=bar");
+// remove an attribute
+uri.removeSearch("hello"); // returns the URI instance for chaining
+// uri == "?foo=bar"
+
+// remove an attribute with value filter
+uri.search("?hello=world&hello=mars&foo=bar");
+uri.removeSearch("hello", "world"); // returns the URI instance for chaining
+// uri == "?hello=mars&foo=bar"
+
+// remove multiple values
+uri.search("?hello=world&hello=mars&foo=bar&mine=true");
+uri.removeSearch(["hello", "foo"]);
+// uri == "?mine=true"
+
+// remove multiple values with value filter
+uri.search("?hello=world&hello=mars&foo=bar&mine=true&a=1&a=2&a=3");
+uri.removeSearch({hello: "world", foo: undefined, a: ["1", "3"]});
+// uri == "?hello=mars&mine=true&a=2"
+
+// remove multiple values with RegExp
+uri.search("?hello=world&hello=mars&foo=bar&mine=true&a=1&a=2&a=3");
+uri.removeSearch(/^hello/);
+// uri == "?foo=bar&mine=true&a=1&a=2&a=3"
+
+// filter values with RegExp
+uri.search("?foo=bar&foo=baz&foo=bam&obj=bam&bar=bar&bar=baz&bar=bam");
+uri.removeSearch('foo', /[rz]$/);
+// uri == "?foo=bam&obj=bam&bar=bar&bar=baz&bar=bam"
+
+
hasSearch(), hasQuery()
+
.hasQuery() is an alias of .hasSearch(). The method checks the existence and value of a given parameter within the query string.
+
var uri = URI("?string=bar&list=one&list=two&number=123&null&empty=");
+
+// check if parameter exists (regardless of value)
+uri.hasQuery("string") === true;
+uri.hasQuery("nono") === false;
+
+// check if parameter has a truthy / falsy value
+uri.hasQuery("string", true) === true;
+uri.hasQuery("string", false) === false;
+uri.hasQuery("empty", true) === false;
+uri.hasQuery("empty", false) === true;
+
+// check if parameter has a given value
+uri.hasQuery("string", "bar") === true;
+uri.hasQuery("number", 123) === true;
+
+// check if value is contained in parameter list
+uri.hasQuery("list", "two", true) === true;
+uri.hasQuery("list", ["two"], true) === true;
+uri.hasQuery("list", "three", true) === false;
+uri.hasQuery("list", ["two", "three"], true) === false;
+uri.hasQuery("list", /ne$/, true) === true;
+
+// check if parameter matches an expression
+uri.hasQuery("string", /ar$/) === true;
+
+// check if parameter name matches an expression
+uri.hasQuery(/^str/) === true;
+// check if parameter name matches an expression and value exists
+uri.hasQuery(/^li/, "two") === true;
+
+// check by comparison function
+uri.hasQuery("string", function(value, name, data) {
+ // value === "bar";
+ // name === "string";
+ // data === uri.query(true);
+ return true;
+}) === true;
+
+
Working with the Fragment (Hash)
+
+
+ There are virtually no limits to what you might do with fragments (hash).
+ Every system has their own bag of tricks.
+ As a result URI.js cannot offer any of the following tools right out of the box.
+ The most common abuse of fragments are storing URLs or query string like data.
+
+
+
+ Usually a prefix is used to identify data with special meaning. This prefix can be pretty much what you want.
+ For URIs it's usually ! and for query-like data it often is ?.
+ But they don't have to, which is why you can define a global default: URI.fragmentPrefix = "$";
+
+
+
Query String Fragments
+
The file src/URI.fragmentQuery.js is a "plugin" that allows you to store data in hashes in the same manner the .query() functions provide.
+
+
var uri = new URI("#?hello=world");
+uri.addFragment("hello", "mars"); // returns the URI instance for chaining
+// uri == "#?hello=world&hello=mars"
+
+// to change the fragment prefix on an instance level:
+uri.fragmentPrefix("!");
+
+// to change the fragment prefix on a global level:
+URI.fragmentPrefix = "!";
+
+
+
URL Fragments
+
The file src/URI.fragmentURI.js is a "plugin" that allows you to store URLs in hashes.
+
+
var uri = URI("http://example.org/#!/foo/bar/baz.html");
+var furi = uri.fragment(true);
+
+// manipulating the fragment URI
+furi.pathname() === "/foo/bar/baz.html";
+furi.pathname("/hello.html");
+
+// has direct effect on the actual URI
+uri.toString() === "http://example.org/#!/hello.html"
+
+// to change the fragment prefix on an instance level:
+uri.fragmentPrefix("?");
+
+// to change the fragment prefix on a global level:
+URI.fragmentPrefix = "?";
+
+
Normalizing URLs
+
+
normalize()
+
executes normalizeProtocol(), normalizeHostname(), normalizePort(), normalizePath(), normalizeSearch(), normalizeHash()
+
+
normalizeProtocol()
+
var uri = new URI("hTTp://www.example.org/");
+// normalize protocol
+uri.normalizeProtocol(); // returns the URI instance for chaining
+// uri == "http://www.example.org/"
+
+
normalizeHostname()
+
For IDN conversion punycode.js must be available (bundled in URI.js).
+ For IPv6-best-notation conversion IPv6.js must be available (bundled in URI.js). Also lower-cases hostnames.
+
var uri = new URI("http://www.exämple.org/");
+// normalize IDN host
+uri.normalizeHostname(); // returns the URI instance for chaining
+// uri == "http://www.xn--exmple-cua.org/"
+
+// normalize IPv6 host
+uri.hostname("fe80:0000:0000:0000:0204:61ff:fe9d:f156");
+uri.normalizeHostname(); // returns the URI instance for chaining
+// uri == "http://fe80::204:61ff:fe9d:f156/"
+
+// normalize hostname to lower case
+uri.hostname("wWw.eXample.Org");
+uri.normalizeHostname(); // returns the URI instance for chaining
+// uri == "http://www.example.org/"
+
There is no .normalizeHost(), as .host() is a property comprised of .hostname() and .port()
+
+
normalizePort()
+
Removes the port, if it's the default for the given protocol (http: 80, https: 443, ftp: 21).
+
The list of default ports can be modified at URI.defaultPorts
+
var uri = new URI("http://example.org:80/foo.html");
+// normalize port
+uri.normalizePort(); // returns the URI instance for chaining
+// uri == "http://example.org/foo.html"
+
+
normalizePathname(), normalizePath()
+
.normalizePath() is an alias of .normalizePathname(), they resolve relative hierarchies
+
var uri = new URI("/hello/foo/woo/.././../world.html");
+// normalize path
+uri.normalizePathname(); // returns the URI instance for chaining
+// uri == "/hello/world.html"
+
+
normalizeSearch(), normalizeQuery()
+
Turns ?&foo=bar&&foo=bar&foo=baz& into ?foo=bar&foo=baz and removes ? if there is no query string.
+
var uri = new URI("?&foo=bar&&foo=bar&foo=baz&");
+// normalize search
+uri.normalizeSearch(); // returns the URI instance for chaining
+// uri == "?foo=bar&foo=baz"
+
+
normalizeHash(), normalizeFragment()
+
removes # if there is no hash
+
var uri = new URI("http://example.org/foo/hello.html#");
+// normalize hash
+uri.normalizeHash(); // returns the URI instance for chaining
+// uri == "http://example.org/bar/world.xml"
+
+
+
Charsets / Encodings
+
+
iso8859()
+
.iso8859() converts unicode-encoded escape sequences to ISO8859-encoded escape sequences. It does this by calling .normalize() internally.
+
var uri = new URI("/%C3%A4.html");
+uri.iso8859(); // returns the URI instance for chaining
+// uri == "/%E4.html"
+
You can make URI work with ISO8859 encoding by default by calling URI.iso8859().
+
+
unicode()
+
.unicode() converts ISO8859-encoded escape sequences to unicode-encoded escape sequences. It does this by calling .normalize() internally.
+
var uri = new URI("/%E4.html");
+uri.unicode(); // returns the URI instance for chaining
+// uri == "/%C3%A4.html"
+
+
+
+
+
readable()
+
Formats URLs to be human readable (much like your browser does nowadays).
+
var uri = new URI("http://foo:bar@www.xn--exmple-cua.org/"
+ + "hello%20world/ä.html?foo%5B%5D=b+är#fragment");
+
+uri.readable() === "http://www.exämple.org/"
+ + "hello world/ä.html?foo[]=b är#fragment";
+
+
+
Relative and Absolute URLs
+
+
relativeTo()
+
.relativeTo() compares two paths and makes one relative to the other
+
var uri = new URI("/relative/path");
+// make path relative
+var relUri = uri.relativeTo("/relative/sub/foo/sub/file"); // returns a new URI instance
+// relUri == "../../../path"
+
+// absolute URLs are passed through unchanged
+URI("http://example.org/world.html")
+ .relativeTo("http://google.com/baz");
+// -> "http://example.org/world.html"
+
+// absolute URLs relative to absolute URLs
+// may resolve the protocol
+URI("http://example.org/world.html")
+ .clone()
+ .authority("")
+ .relativeTo("http://google.com/baz");
+// -> "//google.com/world.html"
+
+// equal URLs are relative by empty string
+URI("http://www.example.com:8080/dir/file")
+ .relativeTo('http://www.example.com:8080/dir/file');
+// -> ""
+
+// relative on fragment and query string as well
+URI("http://www.example.com:8080/dir/file?foo=bar#abcd")
+ .relativeTo('http://www.example.com:8080/dir/file');
+// -> "?foo=bar#abcd"
+
.relativeTo() and .absoluteTo() reverse each other.
+
+
absoluteTo()
+
.absoluteTo() makes a relative path absolute based on another path
+
var uri = new URI("../../../path");
+// make path absolute
+var relUri = uri.absoluteTo("/relative/sub/foo/sub/file"); // returns a new URI instance
+// relUri == "/relative/path"
+
+// resolves protocols
+var u = new URI('//example.com/path');
+u.absoluteTo('https://example.com');
+// -> "https://example.com/path"
+var u = new URI('//example.com/path');
+u.absoluteTo('https://');
+// -> "https://example.com/path"
+
.relativeTo() and .absoluteTo() reverse each other.
+
+
+
Comparing URLs
+
+
equals()
+
.equals() determines if the given URLs are the same - disregarding default ports, capitalization, dot-pathnames, query-parameter order, etc.
+
var a = "http://example.org/foo/bar.html"
+ + "?foo=bar&hello=world&hello=mars#fragment";
+var b;
+
+// normalizing URI before comparison:
+b = "http://exAMPle.org:80/foo/../foo/bar.html"
+ + "?foo=bar&hello=world&hello=mars#fragment";
+
+a !== b;
+URI(a).equals(b) === true;
+
+
+// comparing query string parameters:
+b = "http://example.org/foo/bar.html"
+ + "?hello=mars&foo=bar&hello=world&#fragment";
+
+a !== b;
+URI(a).equals(b) === true;
+
+// shorthand for comparing to window.location.href:
+URI(a).equals();
+
+
+
Parsing URLs
+
+
URI.parse(string url)
+
parses a string into its URI components. returns an object containing the found components
+
var result = URI.parse("http://example.org/foo.html");
+result === {
+ protocol: "http",
+ username: null,
+ password: null,
+ hostname: "example.org",
+ port: null,
+ path: "/foo.html",
+ query: null,
+ fragment: null
+};
+
+
URI.parseAuthority(string url, object parts)
+
parses a string's beginning into its URI components username, password, hostname, port.
+ Found components are appended to the parts parameter.
+ Remaining string is returned
+
var parts = {};
+var result = URI.parseAuthority("user:pass@example.org:8080/foo.html", parts);
+result === "/foo.html";
+parts === {
+ username: "user",
+ password: "pass",
+ hostname: "example.org",
+ port: "8080"
+};
+
+
URI.parseUserinfo(string url, object parts)
+
parses a string's beginning into its URI components username, password.
+ Found components are appended to the parts parameter.
+ Remaining string is returned
+
var parts = {};
+var result = URI.parseUserinfo("user:pass@example.org:8080/foo.html", parts);
+result === "example.org:8080/foo.html";
+parts === {
+ username: "user",
+ password: "pass"
+};
+
+
URI.parseHost(string url, object parts)
+
parses a string's beginning into its URI components hostname, port.
+ Found components are appended to the parts parameter.
+ Remaining string is returned
+
var parts = {};
+var result = URI.parseHost("example.org:8080/foo.html", parts);
+result === "/foo.html";
+parts === {
+ hostname: "example.org",
+ port: "8080"
+};
+
As of v1.19.0 you can activate host name validation (disabled by default):
+
URI.preventInvalidHostname = true;
+var parts = {};
+var result = URI.parseHost("/foo.html", parts);
+// throws TypeError
+
+URI.preventInvalidHostname = false;
+var uri = new URI('http://example.org/')
+ .preventInvalidHostname(true)
+ .hostname('')
+// throws TypeError
+
+
URI.parseQuery(string querystring)
+
Parses the passed query string into an object. Returns object {propertyName: propertyValue}
+
var result = URI.parseQuery("?foo=bar&hello=world&hello=mars&bam=&yup");
+result === {
+ foo: "bar",
+ hello: ["world", "mars"],
+ bam: "",
+ yup: null
+};
+
+
Serializing URLs
+
+
URI.build(object parts)
+
serializes the URI components passed in parts into a URI string
+
var parts = {
+ protocol: "http",
+ username: null,
+ password: null,
+ hostname: "example.org",
+ port: null,
+ path: "/foo.html",
+ query: null,
+ fragment: null
+};
+URI.build(parts) === "http://example.org/foo.html";
+
+
URI.buildAuthority(object parts)
+
serializes the URI components username, password, hostname, port passed in parts into a URI string
+
var parts = {
+ username: "user",
+ password: "pass",
+ hostname: "example.org",
+ port: "8080"
+};
+URI.buildAuthority(parts) === "user:pass@example.org:8080";
+
+
URI.buildUserinfo(object parts)
+
serializes the URI components username, password passed in parts into a URI string
+
var parts = {
+ username: "user",
+ password: "pass"
+};
+URI.buildUserinfo(parts) === "user:pass@";
+
+
URI.buildHost(object parts)
+
serializes the URI components hostname, port passed in parts into a URI string
+
var parts = {
+ hostname: "example.org",
+ port: "8080"
+};
+URI.buildHost(parts) === "example.org:8080";
+
+
URI.buildQuery(object data, [boolean duplicateQueryParameters], [boolean escapeQuerySpace])
+
serializes the query string parameters
+
var data = {
+ foo: "bar",
+ hello: ["world", "mars", "mars"],
+ bam: "",
+ yup: null,
+ removed: undefined
+};
+
+// Note: duplicate hello=mars is dropped (default behavior!)
+URI.buildQuery(data) === "foo=bar&hello=world&hello=mars&bam=&yup";
+
+// Note: duplicate hello=mars is preserved
+URI.buildQuery(data, true) === "foo=bar&hello=world&hello=mars&hello=mars&bam=&yup";
+
To preserve duplicate values, use URI.buildQuery() directly:
+
var uri = new URI("http://example.org/foo.html?bar=baz");
+var data = uri.query(true);
+
+data.some = "new data";
+uri.query(URI.buildQuery(data, true));
+
+// you can also use the static URI.addQuery() and URI.removeQuery()
+URI.addQuery(data, "hello", "world");
+uri.query(URI.buildQuery(data, true));
+
+
As of v1.8.0 you can configure query parameter de/duplication:
+
// make all new URI instances allow duplicates:
+URI.duplicateQueryParameters = true; // default is false
+
+// make a specific URI instance allow duplicates:
+var withDuplicates = URI("?bar=1&bar=1")
+ .duplicateQueryParameters(true)
+ .normalizeQuery()
+ .toString();
+
+// make a specific URI instance avoid duplicates (default):
+var noDuplicates = URI("?bar=1&bar=1")
+ .duplicateQueryParameters(false)
+ .normalizeQuery()
+ .toString();
+
+withDuplicates === "?bar=1&bar=1";
+noDuplicates === "?bar=1";
+
+
As of v1.11.0 you can configure query space en/decoding:
+
// prevent all new URI instances from escaping spaces in query strings:
+URI.escapeQuerySpace = false; // default is true
+
+// make a specific URI instance escape spaces in query:
+var withPlus = URI("?bar=hello+world")
+ .escapeQuerySpace(true)
+ .query(true).bar;
+
+// make a specific URI instance not escape spaces in query
+var withPercent = URI("?bar=hello%20world")
+ .escapeQuerySpace(false)
+ .query(true).bar;
+
+withPlus === "hello world";
+withPercent === "hello world";
+
+
Encoding and Decoding URLs
+
+
URI.encode()
+
Encode an URI component with strict compliance to RFC3986
+
URI.encode("hä lo#w*rl:d!") === "h%C3%A4%20lo%23w%2Arl%3Ad%21";
+// vs.
+encodeURIComponent("hä lo#w*rl:d!") === "h%C3%A4%20lo%23w*rl%3Ad!";
+// not how * and ! were not encoded
+
+
URI.decode()
+
Decode an URI component
+
URI.decode("h%C3%A4%20lo%23w%2Arl%3Ad%21") === "hä lo#w*rl:d!";
+// note:
+URI.decode === decodeURIComponent;
+
+
URI.encodeReserved()
+
Encode an URI component whilst preserving reserved characters
+
URI.encodeReserved("ä:/?#[]@!$&'()*+,;=") === "%C3%A4:/?#[]@!$&'()*+,;=";
+// vs.
+URI.encode("ä:/?#[]@!$&'()*+,;=") ===
+ "%C3%A4%3A%2F%3F%23%5B%5D%40%21%24%26%27%28%29%2A%2B%2C%3B%3D";
+
+
URI.encodeQuery()
+
Encode a query string component. Works like encode(), except it handles %20 as + (space) if URI.escapeQuerySpace = true;.
+
URI.escapeQuerySpace = true; // default
+URI.encodeQuery(" ") === "+";
+
+URI.escapeQuerySpace = false;
+URI.encodeQuery(" ") === "%20";
+
+// vs.
+URI.encode(" ") === "%20";
+
+
URI.decodeQuery()
+
Decode a query string component. Works like decode(), except it handles + as %20 (space) if URI.escapeQuerySpace = true;.
+
URI.escapeQuerySpace = true; // default
+URI.decodeQuery("+") === " ";
+
+URI.escapeQuerySpace = false;
+URI.decodeQuery("+") === "+";
+
+// vs.
+URI.decode("+") === "+";
+
+
+
Static Helper Functions
+
+
URI.noConflict()
+
removes URI variables from global scope
+
// restores window.URI to its previous state and returns URI
+URI.noConflict();
+// restores the global variable to its previous state and returns the object itself
+URITemplate.noConflict();
+IPv6.noConflict();
+SecondLevelDomains.noConflict();
+
+// restore all objects and return them as a map {URI: ..., IPv6: ..., ....}
+URI.noConflict(true);
+
+
URI.addQuery()
+
adds data to a map
+
var data = {};
+
+URI.addQuery(data, "hello", "mars");
+data === {hello: "mars"};
+
+URI.addQuery(data, "hello", "world");
+data === {hello: ["mars", "world"]};
+
+URI.addQuery(data, {foo: "bar", goodbye : ["world", "mars"]});
+data === {hello: ["mars", "world"], foo: "bar", goodbye : ["world", "mars"]};
+
+
URI.removeQuery()
+
removes data from a map
+
var data === {hello: ["mars", "world"], foo: "bar", goodbye : ["world", "mars"]};
+
+URI.removeQuery(data, "hello");
+data === {foo: "bar", goodbye : ["world", "mars"]};
+
+// remove an attribute with value filter
+data = {hello: ["world", "mars"], foo: "bar"};
+URI.removeQuery(data, "hello", "world");
+data === {hello: ["mars"], foo: "bar"} // yes, still an array
+
+// remove multiple values
+data = {hello: ["world", "mars"], foo: "bar", mine: "true"}
+URI.removeQuery(["hello", "foo"]);
+data === {mine: "true"};
+
+// remove multiple values with value filter
+data = {hello: ["world", "mars"], foo: "bar", mine: "true", a: ["1", "2", "3"]}
+URI.removeQuery({hello: "world", foo: undefined, a: ["1", "3"]});
+data === {hello: ["mars"], mine: "true", a: ["2"]}
+
+
URI.commonPath()
+
URI.commonPath() determines the common base directory of two paths.
+
URI.commonPath("/foo/bar/baz.html", "/foo/bar/world.html");
+// returns "/foo/bar/"
+
+URI.commonPath("/foo/bar/baz.html", "/foo/bazz/world.html");
+// returns "/foo/"
+
+URI.commonPath("/foo/bar/baz.html", "/other/world.html");
+// returns "/"
+
+URI.commonPath("/foo", "bar");
+// returns ""
+
+
URI.joinPaths()
+
URI.joinPaths() composes a path from directory tokens.
+
URI.joinPaths('/a/b', '/c', 'd', '/e');
+// returns URI("/a/b/c/d/e")
+
+URI.joinPaths('a/b', 'http://example.com/c', new URI('d/'), '/e');
+// returns URI("a/b/c/d/e")
+
+URI.joinPaths('/a/');
+// returns URI("/a/")
+
+URI.joinPaths('');
+// returns URI("")
+
+URI.joinPaths('', 'a', '');
+// returns URI("/a/")
+
+
URI.withinString()
+
URI.withinString() identifies URIs within text, e.g. to translate them to <a>-Tags. (Obviously you'd want to put the urls inside the href-Attribute and escape them properly…)
+
.withinString() only works on plain text, it will not work with HTML!
+
var source = "Hello www.example.com,\n"
+ + "http://google.com is a search engine, like http://www.bing.com\n"
+ + "http://exämple.org/foo.html?baz=la#bumm is an IDN URL,\n"
+ + "http://123.123.123.123/foo.html is IPv4 and "
+ + "http://fe80:0000:0000:0000:0204:61ff:fe9d:f156/foobar.html is IPv6.\n"
+ + "links can also be in parens (http://example.org) "
+ + "or quotes »http://example.org«.";
+
+var result = URI.withinString(source, function(url) {
+ // callback needs to return a string
+ // feel free to URI(url).normalize().toString() or something
+ return "<a>" + url + "</a>";
+});
+
+/* result is:
+Hello <a>www.example.com</a>,
+<a>http://google.com</a> is a search engine, like <a>http://www.bing.com</a>
+<a>http://exämple.org/foo.html?baz=la#bumm</a> is an IDN URL,
+<a>http://123.123.123.123/foo.html</a> is IPv4 and <a>http://fe80:0000:0000:0000:0204:61ff:fe9d:f156/foobar.html</a> is IPv6.
+links can also be in parens (<a>http://example.org</a>) or quotes »<a>http://example.org</a>«.
+*/
+
+// a proper replacement could look like the following:
+var escapeHtml = function(string) {
+ return string
+ .replace(/&/g, "&")
+ .replace(/</g, "<")
+ .replace(/>/g, ">")
+ .replace(/"/g, """);
+};
+var result = URI.withinString(source, function(url) {
+ var uri = new URI(url);
+ uri.normalize();
+ return "<a href="" + escapeHtml(uri) + "">"
+ + escapeHtml(uri.readable()) + "</a>";
+});
+
+
As of URI.js 1.12.0 withinString accepts the following parameters:
+
var source = "Hello www.example.com.";
+var decorate = function(url) {
+ return "<code>" + url + "</code>";
+};
+var result = null;
+
+// access to the original input text from the callback
+URI.withinString(source, function(url, start, end, source) {
+ source.slice(start, end) === url;
+ return url;
+});
+
+// ignore certain URLs
+source = "Hello www.example.com,\n"
+ + "ohgodno://example.org/ is a a protocol we want ignored";
+result = URI.withinString(source, decorate, {
+ ignore: /^ohgodno:/i
+});
+
+/* result is:
+Hello <code>www.example.com</code>,
+ohgodno://example.org/ is a a protocol we want ignored
+*/
+
+// ignore URLs in HTML
+source = "Hello www.example.com,\n"
+ + '<img src="http://example.org/image.png" alt=""> is HTML,\n'
+ + "<a href='http://example.org/target.html'> is HTML</a>,\n"
+ + "<a href=http://example.org/target.html> is HTML, too</a>.";
+result = URI.withinString(source, decorate, {
+ ignoreHtml: true
+});
+
+/* result is:
+Hello <code>www.example.com</code>,
+<img src="http://example.org/image.png" alt=""> is HTML,
+<a href='http://example.org/target.html'> is HTML</a>,
+<a href=http://example.org/target.html> is HTML, too</a>
+*/
+
+// custom URI beginning pattern
+source = "That example.com/ is just a domain";
+result = URI.withinString(source, decorate, {
+ // "scheme://" or "www." or "domain.tld/"
+ start: /\b(?:([a-z][a-z0-9.+-]*:\/\/)|www\.|[a-z]+\.[a-z]{2,4}\/)/gi
+});
+
+/* result is:
+That <code>example.com/</code> is just a domain
+*/
+
+
URI.iso8859()
+
URI.iso8859() tells URI.js to use the older escape/unescape methods, for backwards compatibility with non-unicode platforms.
+
URI.iso8859();
+
+var uri = new URI("http://example.org/foo/æ.html");
+// http://example.org/foo/%E6.html
+
+
URI.unicode()
+
URI.unicode() restores the default unicode-encoded URLs.
+
URI.unicode();
+
+var uri = new URI("http://example.org/foo/æ.html");
+// http://example.org/foo/%C3%A6.html
+
+
URI.expand()
+
URI.expand() is a convenience wrapper for URITemplate.
+ While URITemplate#expand returns a string, URI.expand() returns an URI instance.
+
URI.expand("/foo/{var}/{iable}", {
+ "var": "bar",
+ "iable": "hello world.html"
+});
+
+// returns URI("/foo/bar/hello%20world.html")
+
+
+
+
+
diff --git a/mayan/apps/appearance/static/appearance/vendors/URI.js-1.19.1/index.html b/mayan/apps/appearance/static/appearance/vendors/URI.js-1.19.1/index.html
new file mode 100644
index 0000000000..880a24fbd0
--- /dev/null
+++ b/mayan/apps/appearance/static/appearance/vendors/URI.js-1.19.1/index.html
@@ -0,0 +1,173 @@
+
+
+
+