(function(){ var levels = { // http://tools.ietf.org/html/rfc6570#section-1.2 "Level 1" : { expressions : { 'Simple string expansion' : { "{var}" : "value", "{hello}" : "Hello%20World%21" } }, values : { "var" : "value", "hello" : "Hello World!" } }, "Level 2" : { expressions : { 'Reserved string expansion' : { "{+var}" : "value", "{+hello}" : "Hello%20World!", "{+path}/here" : "/foo/bar/here", "here?ref={+path}" : "here?ref=/foo/bar" }, 'Fragment expansion, crosshatch-prefixed' : { "X{#var}" : "X#value", "X{#hello}" : "X#Hello%20World!" } }, values : { "var" : "value", "hello" : "Hello World!", "path" : "/foo/bar" } }, "Level 3" : { expressions : { "String expansion with multiple variables" : { "map?{x,y}" : "map?1024,768", "{x,hello,y}" : "1024,Hello%20World%21,768" }, "Reserved expansion with multiple variables" : { "{+x,hello,y}" : "1024,Hello%20World!,768", "{+path,x}/here" : "/foo/bar,1024/here" }, "Fragment expansion with multiple variables" : { "{#x,hello,y}" : "#1024,Hello%20World!,768", "{#path,x}/here" : "#/foo/bar,1024/here" }, "Label expansion, dot-prefixed" : { "X{.var}" : "X.value", "X{.x,y}" : "X.1024.768" }, "Path segments, slash-prefixed" : { "{/var}" : "/value", "{/var,x}/here" : "/value/1024/here" }, "Path-style parameters, semicolon-prefixed" : { "{;x,y}" : ";x=1024;y=768", "{;x,y,empty}" : ";x=1024;y=768;empty" }, "Form-style query, ampersand-separated" : { "{?x,y}" : "?x=1024&y=768", "{?x,y,empty}" : "?x=1024&y=768&empty=" }, "Form-style query continuation" : { "?fixed=yes{&x}" : "?fixed=yes&x=1024", "{&x,y,empty}" : "&x=1024&y=768&empty=" } }, values : { "var" : "value", "hello" : "Hello World!", "empty" : "", "path" : "/foo/bar", "x" : "1024", "y" : "768" } }, "Level 4" : { expressions : { "String expansion with value modifiers" : { "{var:3}" : "val", "{var:30}" : "value", "{list}" : "red,green,blue", "{list*}" : "red,green,blue", "{keys}" : "semi,%3B,dot,.,comma,%2C", "{keys*}" : "semi=%3B,dot=.,comma=%2C" }, "Reserved expansion with value modifiers" : { "{+path:6}/here" : "/foo/b/here", "{+list}" : "red,green,blue", "{+list*}" : "red,green,blue", "{+keys}" : "semi,;,dot,.,comma,,", "{+keys*}" : "semi=;,dot=.,comma=," }, "Fragment expansion with value modifiers" : { "{#path:6}/here" : "#/foo/b/here", "{#list}" : "#red,green,blue", "{#list*}" : "#red,green,blue", "{#keys}" : "#semi,;,dot,.,comma,,", "{#keys*}" : "#semi=;,dot=.,comma=," }, "Label expansion, dot-prefixed" : { "X{.var:3}" : "X.val", "X{.list}" : "X.red,green,blue", "X{.list*}" : "X.red.green.blue", "X{.keys}" : "X.semi,%3B,dot,.,comma,%2C", "X{.keys*}" : "X.semi=%3B.dot=..comma=%2C" }, "Path segments, slash-prefixed" : { "{/var:1,var}" : "/v/value", "{/list}" : "/red,green,blue", "{/list*}" : "/red/green/blue", "{/list*,path:4}" : "/red/green/blue/%2Ffoo", "{/keys}" : "/semi,%3B,dot,.,comma,%2C", "{/keys*}" : "/semi=%3B/dot=./comma=%2C" }, "Path-style parameters, semicolon-prefixed" : { "{;hello:5}" : ";hello=Hello", "{;list}" : ";list=red,green,blue", "{;list*}" : ";list=red;list=green;list=blue", "{;keys}" : ";keys=semi,%3B,dot,.,comma,%2C", "{;keys*}" : ";semi=%3B;dot=.;comma=%2C" }, "Form-style query, ampersand-separated" : { "{?var:3}" : "?var=val", "{?list}" : "?list=red,green,blue", "{?list*}" : "?list=red&list=green&list=blue", "{?keys}" : "?keys=semi,%3B,dot,.,comma,%2C", "{?keys*}" : "?semi=%3B&dot=.&comma=%2C" }, "Form-style query continuation" : { "{&var:3}" : "&var=val", "{&list}" : "&list=red,green,blue", "{&list*}" : "&list=red&list=green&list=blue", "{&keys}" : "&keys=semi,%3B,dot,.,comma,%2C", "{&keys*}" : "&semi=%3B&dot=.&comma=%2C" } }, values : { "var" : "value", "hello" : "Hello World!", "path" : "/foo/bar", "list" : ["red", "green", "blue"], "keys" : { "semi" : ";", "dot" : ".", "comma" : "," } } }, // http://tools.ietf.org/html/rfc6570#section-3 "Expression Expansion" : { expressions : { "Variable Expansion" : { "{count}" : "one,two,three", "{count*}" : "one,two,three", "{/count}" : "/one,two,three", "{/count*}" : "/one/two/three", "{;count}" : ";count=one,two,three", "{;count*}" : ";count=one;count=two;count=three", "{?count}" : "?count=one,two,three", "{?count*}" : "?count=one&count=two&count=three", "{&count*}" : "&count=one&count=two&count=three" }, "Simple String Expansion" : { "{var}" : "value", "{hello}" : "Hello%20World%21", "{half}" : "50%25", "O{empty}X" : "OX", "O{undef}X" : "OX", "{x,y}" : "1024,768", "{x,hello,y}" : "1024,Hello%20World%21,768", "?{x,empty}" : "?1024,", "?{x,undef}" : "?1024", "?{undef,y}" : "?768", "{var:3}" : "val", "{var:30}" : "value", "{list}" : "red,green,blue", "{list*}" : "red,green,blue", "{keys}" : "semi,%3B,dot,.,comma,%2C", "{keys*}" : "semi=%3B,dot=.,comma=%2C" }, "Reserved Expansion" : { "{+var}" : "value", "{+hello}" : "Hello%20World!", "{+half}" : "50%25", "{base}index" : "http%3A%2F%2Fexample.com%2Fhome%2Findex", "{+base}index" : "http://example.com/home/index", "O{+empty}X" : "OX", "O{+undef}X" : "OX", "{+path}/here" : "/foo/bar/here", "here?ref={+path}" : "here?ref=/foo/bar", "up{+path}{var}/here" : "up/foo/barvalue/here", "{+x,hello,y}" : "1024,Hello%20World!,768", "{+path,x}/here" : "/foo/bar,1024/here", "{+path:6}/here" : "/foo/b/here", "{+list}" : "red,green,blue", "{+list*}" : "red,green,blue", "{+keys}" : "semi,;,dot,.,comma,,", "{+keys*}" : "semi=;,dot=.,comma=," }, "Fragment Expansion" : { "{#var}" : "#value", "{#hello}" : "#Hello%20World!", "{#half}" : "#50%25", "foo{#empty}" : "foo#", "foo{#undef}" : "foo", "{#x,hello,y}" : "#1024,Hello%20World!,768", "{#path,x}/here" : "#/foo/bar,1024/here", "{#path:6}/here" : "#/foo/b/here", "{#list}" : "#red,green,blue", "{#list*}" : "#red,green,blue", "{#keys}" : "#semi,;,dot,.,comma,,", "{#keys*}" : "#semi=;,dot=.,comma=," }, "Label Expansion with Dot-Prefix" : { "{.who}" : ".fred", "{.who,who}" : ".fred.fred", "{.half,who}" : ".50%25.fred", "www{.dom*}" : "www.example.com", "X{.var}" : "X.value", "X{.empty}" : "X.", "X{.undef}" : "X", "X{.var:3}" : "X.val", "X{.list}" : "X.red,green,blue", "X{.list*}" : "X.red.green.blue", "X{.keys}" : "X.semi,%3B,dot,.,comma,%2C", "X{.keys*}" : "X.semi=%3B.dot=..comma=%2C", "X{.empty_keys}" : "X", "X{.empty_keys*}" : "X" }, "Path Segment Expansion" : { "{/who}" : "/fred", "{/who,who}" : "/fred/fred", "{/half,who}" : "/50%25/fred", "{/who,dub}" : "/fred/me%2Ftoo", "{/var}" : "/value", "{/var,empty}" : "/value/", "{/var,undef}" : "/value", "{/var,x}/here" : "/value/1024/here", "{/var:1,var}" : "/v/value", "{/list}" : "/red,green,blue", "{/list*}" : "/red/green/blue", "{/list*,path:4}" : "/red/green/blue/%2Ffoo", "{/keys}" : "/semi,%3B,dot,.,comma,%2C", "{/keys*}" : "/semi=%3B/dot=./comma=%2C" }, "Path-Style Parameter Expansion" : { "{;who}" : ";who=fred", "{;half}" : ";half=50%25", "{;empty}" : ";empty", "{;v,empty,who}" : ";v=6;empty;who=fred", "{;v,bar,who}" : ";v=6;who=fred", "{;x,y}" : ";x=1024;y=768", "{;x,y,empty}" : ";x=1024;y=768;empty", "{;x,y,undef}" : ";x=1024;y=768", "{;hello:5}" : ";hello=Hello", "{;list}" : ";list=red,green,blue", "{;list*}" : ";list=red;list=green;list=blue", "{;keys}" : ";keys=semi,%3B,dot,.,comma,%2C", "{;keys*}" : ";semi=%3B;dot=.;comma=%2C" }, "Form-Style Query Expansion" : { "{?who}" : "?who=fred", "{?half}" : "?half=50%25", "{?x,y}" : "?x=1024&y=768", "{?x,y,empty}" : "?x=1024&y=768&empty=", "{?x,y,undef}" : "?x=1024&y=768", "{?var:3}" : "?var=val", "{?list}" : "?list=red,green,blue", "{?list*}" : "?list=red&list=green&list=blue", "{?keys}" : "?keys=semi,%3B,dot,.,comma,%2C", "{?keys*}" : "?semi=%3B&dot=.&comma=%2C" }, "Form-Style Query Continuation" : { "{&who}" : "&who=fred", "{&half}" : "&half=50%25", "?fixed=yes{&x}" : "?fixed=yes&x=1024", "{&x,y,empty}" : "&x=1024&y=768&empty=", "{&x,y,undef}" : "&x=1024&y=768", "{&var:3}" : "&var=val", "{&list}" : "&list=red,green,blue", "{&list*}" : "&list=red&list=green&list=blue", "{&keys}" : "&keys=semi,%3B,dot,.,comma,%2C", "{&keys*}" : "&semi=%3B&dot=.&comma=%2C" } }, values : { "count" : ["one", "two", "three"], "dom" : ["example", "com"], "dub" : "me/too", "hello" : "Hello World!", "half" : "50%", "var" : "value", "who" : "fred", "base" : "http://example.com/home/", "path" : "/foo/bar", "list" : ["red", "green", "blue"], "keys" : { "semi" : ";", "dot" : ".", "comma" : "," }, "v" : "6", "x" : "1024", "y" : "768", "empty" : "", "empty_keys" : [], "undef" : null } } }; module("URITemplate"); // [].forEach() no IE, lacking interest in polyfilling this... for (var i in levels) { (function(level, data){ test(level, function() { var combined_expression = "", combined_expansion = "", template, expression, expansion; for (type in data.expressions) { for (expression in data.expressions[type]) { combined_expression += "/" + expression; combined_expansion += "/" + data.expressions[type][expression]; template = new URITemplate(expression); expansion = template.expand(data.values); equal(expansion, data.expressions[type][expression], type + ": " + expression); } } template = new URITemplate(combined_expression); expansion = template.expand(data.values); equal(expansion, combined_expansion, type + ": combined"); }); })(i, levels[i]); } test("Data Callbacks", function() { var template = new URITemplate("{var}"), global = function(key) { var data = {"var": "hello world.html"}; return data[key]; }, local = function(key) { return "hello world.html"; }; equal(template.expand(global), "hello%20world.html", "global callback"); equal(template.expand({"var": local}), "hello%20world.html", "local callback"); }); test("Parse errors", function() { raises(function() { URITemplate("AB{var$}IJ").parse(); }, Error, "Failing invalid variable name"); raises(function() { URITemplate("AB{$var}IJ").parse(); }, Error, "Failing invalid operator"); raises(function() { URITemplate("AB{var:3IJ").parse(); }, Error, "Failing missing closing }"); raises(function() { URITemplate("AB{var:3*}IJ").parse(); }, Error, "Failing invalid modifier"); }); test("noConflict mode", function() { var actual_lib = URITemplate; // actual library; after loading, before noConflict() var unconflicted = URITemplate.noConflict(); strictEqual( unconflicted, actual_lib, "noConflict() returns the URITemplate object" ); strictEqual( URITemplate, URITemplate_pre_lib, "noConflict() restores the `URITemplate` variable" ); // restore for other tests window.URITemplate = actual_lib; }); })();