(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;
});

})();