diff --git a/route/route.js b/route/route.js index 83167f053a5..c654c3efac2 100644 --- a/route/route.js +++ b/route/route.js @@ -27,13 +27,25 @@ steal('can/observe', 'can/util/string/deparam', function() { // variables are present in the data, the number of matches is returned // to allow discerning between general and more specific routes. matchesData = function(route, data) { - var count = 0, i = 0; + var count = 0, i = 0, defaults = {}; + // look at default values, if they match ... + for( var name in route.defaults ) { + if(route.defaults[name] === data[name]){ + // mark as matched + defaults[name] = 1; + count++; + } + } for (; i < route.names.length; i++ ) { if (!data.hasOwnProperty(route.names[i]) ) { return -1; } - count++; + if(!defaults[route.names[i]]){ + count++; + } + } + return count; }, onready = !0, @@ -102,17 +114,26 @@ steal('can/observe', 'can/util/string/deparam', function() { // Need to have at least 1 match. matches = 0, matchCount, - routeName = data.route; + routeName = data.route, + propCount = 0; + delete data.route; // If we have a route name in our `can.route` data, use it. if ( ! ( routeName && (route = can.route.routes[routeName]))){ + each(data, function(){propCount++}); // Otherwise find route. each(can.route.routes, function(temp, name){ + // best route is the first with all defaults matching + + matchCount = matchesData(temp, data); if ( matchCount > matches ) { route = temp; matches = matchCount } + if(matchCount >= propCount){ + return false; + } }); } @@ -394,9 +415,11 @@ steal('can/observe', 'can/util/string/deparam', function() { can.route.bind("change", function() { clearTimeout( timer ); timer = setTimeout(function() { - location.hash = "#!" + can.route.param(can.route.data.serialize()) + var serialized = can.route.data.serialize(); + delete serialized.route; + location.hash = "#!" + can.route.param(serialized) }, 1); }); // `onready` event... can.bind.call(document,"ready",can.route.ready); -}); +}); \ No newline at end of file diff --git a/route/route_test.js b/route/route_test.js index f224d2f63bb..39a08fa8a97 100644 --- a/route/route_test.js +++ b/route/route_test.js @@ -195,6 +195,16 @@ test("param-deparam", function(){ same(data, obj) }) +test("deparam-param", function(){ + can.route.routes = {}; + can.route(":foo/:bar",{foo: 1, bar: 2}); + var res = can.route.param({foo: 1, bar: 2}); + equals(res,"/","empty slash") + + var deparamed = can.route.deparam("/") + same(deparamed, {foo: 1, bar: 2, route: ":foo/:bar"}) +}) + test("precident", function(){ can.route.routes = {}; can.route(":who",{who: "index"}); @@ -223,7 +233,7 @@ test("precident", function(){ "can.Control" ); }) -test("precident2", function(){ +test("better matching precident", function(){ can.route.routes = {}; can.route(":type",{who: "index"}); can.route(":type/:id"); @@ -282,6 +292,8 @@ test("updating the hash", function(){ equal(after,"#!bar/"+encodeURIComponent("\/")); start(); + can.remove(can.$(iframe)) + },30); } var iframe = document.createElement('iframe'); @@ -289,6 +301,36 @@ test("updating the hash", function(){ can.$("#qunit-test-area")[0].appendChild(iframe); }); +test("unsticky routes", function(){ + stop(); + window.routeTestReady = function(iCanRoute, loc){ + iCanRoute(":type") + iCanRoute(":type/:id"); + iCanRoute.attr({type: "bar"}); + + setTimeout(function(){ + + iCanRoute.attr({type: "bar", id: "\/"}); + + setTimeout(function(){ + var after = loc.href.substr(loc.href.indexOf("#")); + equal(after,"#!bar/"+encodeURIComponent("\/")); + start(); + + can.remove(can.$(iframe)) + + },30); + + },30) + + + } + var iframe = document.createElement('iframe'); + iframe.src = steal.root.join("can/route/testing.html"); + can.$("#qunit-test-area")[0].appendChild(iframe); +}); + + test("empty default is matched even if last", function(){ can.route.routes = {}; @@ -301,3 +343,47 @@ test("empty default is matched even if last", function(){ route: "" }); }); + + +test("order matched", function(){ + can.route.routes = {}; + can.route(":foo"); + can.route(":bar") + + var obj = can.route.deparam("abc"); + same(obj, { + foo : "abc", + route: ":foo" + }); +}); + +test("param order matching", function(){ + can.route.routes = {}; + can.route("",{ + bar: "foo" + }); + can.route("something/:bar"); + var res = can.route.param({bar: "foo"}); + equal(res, "", "picks the shortest, best match"); + + // picks the first that matches everything ... + can.route.routes = {}; + + can.route(":recipe",{ + recipe: "recipe1", + task: "task3" + }); + + can.route(":recipe/:task",{ + recipe: "recipe1", + task: "task3" + }); + + res = can.route.param({recipe: "recipe1", task: "task3"}); + + equals(res, "", "picks the first match of everything"); + + res = can.route.param({recipe: "recipe1", task: "task2"}); + equals(res,"/task2") +}) +