URI.js - URI-Template
Description: URI.js is a Javascript library for working with URLs.

# URI.js

## URI Template

As of version 1.7.0 URI.js includes an implementation of URI Templates, as specified in RFC 6570 (Level 4, March 2012).

## Using URI Templates

    // creating a new URI Template
    var template = new URITemplate("http://example.org/{file}");
    var result = template.expand({file: "hello world.html"});
    result === "http://example.org/hello%20world.html";

    // of course you can call the constructor like a function and chain things:
    result = URITemplate("http://example.org/{file}")
      .expand({file: "hello world.html"});
    result === "http://example.org/hello%20world.html";

    // access via URI
    result = URI.expand("http://example.org/{file}", {file: "hello world.html"});
    // result == new URI("http://example.org/hello%20world.html");

    // expand() accepts data-callbacks:
    template.expand(function(key) {
        var data = {file: "hello world.html"};
        return data[key];
    });

    // expand() accepts key-callbacks:
    template.expand({file : function(key) {
        return "hello world.html";
    }});

    // Using strict mode
    var template = new URITemplate("http://example.org/{file}");
    var result = template.expand({filename: "hello world.html"}, { strict: true });
    // Uncaught Error: Missing expansion value for variable "file"

## URI Template Syntax

Expressions are placeholders which are to be substituted by the values their variables reference.

- `http://example.org/~{username}/`
- `http://example.org/dictionary/{term:1}/{term}`
- `http://example.org/search{?q*,lang}`

An expression consists of an operator and a (comma-separated) list of variable-specifications. A variable-specification consists of a variable and an optional modifier.

Given the template

    http://example.org/~{username}/{term:1}/{term}{?q*,lang}

and the following data:

    {username: "rodneyrehm", term: "hello world", q: {a: "mars", b: "jupiter"}, lang: "en"}

the expansion looks as follows:

    "http://example.org/~rodneyrehm/h/hello%20world?a=mars&b=jupiter&lang=en"

List of supported operators:

Operator | Description
--- | ---
`None` | Simple String Expansion;
`+` | Reserved character strings;
`#` | Fragment identifiers prefixed by "#";
`.` | Name labels or extensions prefixed by ".";
`/` | Path segments prefixed by "/";
`;` | Path parameter name or name=value pairs prefixed by ";";
`?` | Query component beginning with "?" and consisting of name=value pairs separated by "&"; and,
`&` | Continuation of query-style &name=value pairs within a literal query component.

List of supported modifiers:

Modifier | Description
--- | ---
`None` | No modification, arrays and objects are joined with ","
`*` | Explode arrays and objects (see tables below)
`:3` | Substring of the first 3 characters of the variable's value

### Strings and Numbers

Given `{"var": "hello[world]"}`, the expression `{var}` expands to `hello%5Bworld%5D`. The following table shows an output matrix for every possible operator/modifier combination produced for `string` input.

| Modifier
--- | ---
Operator | None | * | :2
`None` | `hello%5Bworld%5D` | `hello%5Bworld%5D` | `he`
`+` | `hello[world]` | `hello[world]` | `he`
`#` | `#hello[world]` | `#hello[world]` | `#he`
`.` | `.hello%5Bworld%5D` | `.hello%5Bworld%5D` | `.he`
`/` | `/hello%5Bworld%5D` | `/hello%5Bworld%5D` | `/he`
`;` | `;var=hello%5Bworld%5D` | `;var=hello%5Bworld%5D` | `;var=he`
`?` | `?var=hello%5Bworld%5D` | `?var=hello%5Bworld%5D` | `?var=he`
`&` | `&var=hello%5Bworld%5D` | `&var=hello%5Bworld%5D` | `&var=he`

### Arrays

Given `{"var": ["one", "two", "three"]}`, the expression `{var}` expands to `one,two,three`. The following table shows an output matrix for every possible operator/modifier combination produced for `array` input.

| Modifier
--- | ---
Operator | None | * | :2
`None` | `one,two,three` | `one,two,three` | `on,tw,th`
`+` | `one,two,three` | `one,two,three` | `on,tw,th`
`#` | `#one,two,three` | `#one,two,three` | `#on,tw,th`
`.` | `.one,two,three` | `.one.two.three` | `.on,tw,th`
`/` | `/one,two,three` | `/one/two/three` | `/on,tw,th`
`;` | `;var=one,two,three` | `;var=one;var=two;var=three` | `;var=on,tw,th`
`?` | `?var=one,two,three` | `?var=one&var=two&var=three` | `?var=on,tw,th`
`&` | `&var=one,two,three` | `&var=one&var=two&var=three` | `&var=on,tw,th`

### Objects ("plain objects" / "hash maps")

Given `{"var": {"one": "alpha", "two": "bravo"}}`, the expression `{var}` expands to `one,two,three`. The following table shows an output matrix for every possible operator/modifier combination produced for `object` input.

| Modifier
--- | ---
Operator | None | * | :2
`None` | `one,alpha,two,bravo` | `one=alpha,two=bravo` | `on,al,tw,br`
`+` | `one,alpha,two,bravo` | `one=alpha,two=bravo` | `on,al,tw,br`
`#` | `#one,alpha,two,bravo` | `#one=alpha,two=bravo` | `#on,al,tw,br`
`.` | `.one,alpha,two,bravo` | `.one=alpha.two=bravo` | `.on,al,tw,br`
`/` | `/one,alpha,two,bravo` | `/one=alpha/two=bravo` | `/on,al,tw,br`
`;` | `;var=one,alpha,two,bravo` | `;one=alpha;two=bravo` | `;var=on,al,tw,br`
`?` | `?var=one,alpha,two,bravo` | `?one=alpha&two=bravo` | `?var=on,al,tw,br`
`&` | `&var=one,alpha,two,bravo` | `&one=alpha&two=bravo` | `&var=on,al,tw,br`

## Limitations

URI Template is a Proposed Standard and because of that I did not want to deviate from it. That said I'm not at all happy with how the specification turned out. Here are some of my thoughts:

- The explode modifier works the wrong way. `{?some_object}` should lead to `?foo=bar&hello=world`, as this is the common expansion
- The prefix modifier (which I would've named truncate modifier) only has an end-offset. The specification says it's »used to partition an identifier space hierarchically«. `abc` may become `a/bc` or `a/ab/abc`. But there is no way of modifying output to `a/b/c` or `a/b/abc`. Whenever I had to partition identifier spaces, I used one of the latter patterns.
- Operators like `.` automatically prefix the expansion. So `{"var": ["filename", "extension"]}` and `{.var*}` results in `.filename.extension` - obviously not what I wanted.
- Variable names (varname) may only contain `ALPHA / DIGIT / "_" / pct-encoded` and may not be decoded for resolving the reference. This simply feels weird, especially the "may not be decoded" part.
- Other possible modifiers could include some simple character-munging like UPPERCASE, LOWERCASE, CAPITALCASE
- `{/var,empty,empty}` results in `/foobar//` - clearly not what one intended
- `{var}` and `{"var" : {"a": "1", "b": "2"}}` results in `a,1,b,2` - excusemewhat? I would've expected `a=1,b=2` or `a:1,b:2` (in a perverse parallel universe).
- Spaces in the query string should be encoded to `+`, not `%20` according to application/x-www-form-urlencoded