Merge pull request #2890 from braddunbar/search-params

Search params
This commit is contained in:
brad dunbar
2014-01-06 14:21:47 -08:00
3 changed files with 87 additions and 19 deletions

View File

@@ -1249,7 +1249,7 @@
var router = this;
Backbone.history.route(route, function(fragment) {
var args = router._extractParameters(route, fragment);
callback && callback.apply(router, args);
router.execute(callback, args);
router.trigger.apply(router, ['route:' + name].concat(args));
router.trigger('route', name, args);
Backbone.history.trigger('route', router, name, args);
@@ -1257,6 +1257,12 @@
return this;
},
// Execute a route handler with the provided parameters. This is an
// excellent place to do pre-route setup or post-route cleanup.
execute: function(callback, args) {
if (callback) callback.apply(this, args);
},
// Simple proxy to `Backbone.history` to save a fragment into the history.
navigate: function(fragment, options) {
Backbone.history.navigate(fragment, options);
@@ -1281,10 +1287,10 @@
route = route.replace(escapeRegExp, '\\$&')
.replace(optionalParam, '(?:$1)?')
.replace(namedParam, function(match, optional) {
return optional ? match : '([^\/]+)';
return optional ? match : '([^/?]+)';
})
.replace(splatParam, '(.*?)');
return new RegExp('^' + route + '$');
.replace(splatParam, '([^?]*?)');
return new RegExp('^' + route + '(?:\\?(.*))?$');
},
// Given a route, and a URL fragment that it matches, return the array of
@@ -1292,7 +1298,9 @@
// treated as `null` to normalize cross-browser behavior.
_extractParameters: function(route, fragment) {
var params = route.exec(fragment).slice(1);
return _.map(params, function(param) {
return _.map(params, function(param, i) {
// Don't decode the search params.
if (i === params.length - 1) return param || null;
return param ? decodeURIComponent(param) : null;
});
}
@@ -1330,8 +1338,8 @@
// Cached regex for removing a trailing slash.
var trailingSlash = /\/$/;
// Cached regex for stripping urls of hash and query.
var pathStripper = /[?#].*$/;
// Cached regex for stripping urls of hash.
var pathStripper = /#.*$/;
// Has the history handling already been started?
History.started = false;
@@ -1343,6 +1351,11 @@
// twenty times a second.
interval: 50,
// Are we at the app root?
atRoot: function() {
return this.location.pathname.replace(/[^\/]$/, '$&/') === this.root;
},
// Gets the true hash value. Cannot use location.hash directly due to bug
// in Firefox where location.hash will always be decoded.
getHash: function(window) {
@@ -1355,7 +1368,7 @@
getFragment: function(fragment, forcePushState) {
if (fragment == null) {
if (this._hasPushState || !this._wantsHashChange || forcePushState) {
fragment = this.location.pathname;
fragment = this.location.pathname + this.location.search;
var root = this.root.replace(trailingSlash, '');
if (!fragment.indexOf(root)) fragment = fragment.slice(root.length);
} else {
@@ -1405,7 +1418,6 @@
// opened by a non-pushState browser.
this.fragment = fragment;
var loc = this.location;
var atRoot = loc.pathname.replace(/[^\/]$/, '$&/') === this.root;
// Transition from hashChange to pushState or vice versa if both are
// requested.
@@ -1413,17 +1425,17 @@
// If we've started off with a route from a `pushState`-enabled
// browser, but we're currently in a browser that doesn't support it...
if (!this._hasPushState && !atRoot) {
if (!this._hasPushState && !this.atRoot()) {
this.fragment = this.getFragment(null, true);
this.location.replace(this.root + this.location.search + '#' + this.fragment);
this.location.replace(this.root + '#' + this.fragment);
// Return immediately as browser will do redirect to new url
return true;
// Or if we've started out with a hash-based route, but we're currently
// in a browser where it could be `pushState`-based instead...
} else if (this._hasPushState && atRoot && loc.hash) {
} else if (this._hasPushState && this.atRoot() && loc.hash) {
this.fragment = this.getHash().replace(routeStripper, '');
this.history.replaceState({}, document.title, this.root + this.fragment + loc.search);
this.history.replaceState({}, document.title, this.root + this.fragment);
}
}
@@ -1483,7 +1495,7 @@
var url = this.root + (fragment = this.getFragment(fragment || ''));
// Strip the fragment of the query and hash for matching.
// Strip the hash for matching.
fragment = fragment.replace(pathStripper, '');
if (this.fragment === fragment) return;

View File

@@ -4,10 +4,16 @@
var ajax = Backbone.ajax;
var emulateHTTP = Backbone.emulateHTTP;
var emulateJSON = Backbone.emulateJSON;
var history = window.history;
var pushState = history.pushState;
var replaceState = history.replaceState;
QUnit.testStart(function() {
var env = this.config.current.testEnvironment;
// We never want to actually call these during tests.
history.pushState = history.replaceState = function(){};
// Capture ajax settings for comparison.
Backbone.ajax = function(settings) {
env.ajaxSettings = settings;
@@ -30,6 +36,8 @@
Backbone.ajax = ajax;
Backbone.emulateHTTP = emulateHTTP;
Backbone.emulateJSON = emulateJSON;
history.pushState = pushState;
history.replaceState = replaceState;
});
})();

View File

@@ -90,7 +90,7 @@
":repo/compare/*from...*to": "github",
"decode/:named/*splat": "decode",
"*first/complex-*part/*rest": "complex",
":entity?*args": "query",
"query/:entity": "query",
"function/:value": ExternalObject.routingFunction,
"*anything": "anything"
},
@@ -211,6 +211,11 @@
equal(router.page, '20');
});
test("routes via navigate with params", 1, function() {
Backbone.history.navigate('query/test?a=b', {trigger: true});
equal(router.queryArgs, 'a=b');
});
test("routes via navigate for backwards-compatibility", 2, function() {
Backbone.history.navigate('search/manhattan/p20', true);
equal(router.query, 'manhattan');
@@ -288,7 +293,7 @@
});
test("routes (query)", 5, function() {
location.replace('http://example.com#mandel?a=b&c=d');
location.replace('http://example.com#query/mandel?a=b&c=d');
Backbone.history.checkUrl();
equal(router.entity, 'mandel');
equal(router.queryArgs, 'a=b&c=d');
@@ -538,7 +543,7 @@
Backbone.history.stop();
location.replace('http://example.com/root/x/y?a=b');
location.replace = function(url) {
strictEqual(url, '/root/?a=b#x/y');
strictEqual(url, '/root/#x/y?a=b');
};
Backbone.history = _.extend(new Backbone.History, {
location: location,
@@ -555,7 +560,7 @@
test("#1695 - hashChange to pushState with search.", 1, function() {
Backbone.history.stop();
location.replace('http://example.com/root?a=b#x/y');
location.replace('http://example.com/root#x/y?a=b');
Backbone.history = _.extend(new Backbone.History, {
location: location,
history: {
@@ -604,7 +609,7 @@
test("#2062 - Trigger 'route' event on router instance.", 2, function() {
router.on('route', function(name, args) {
strictEqual(name, 'routeEvent');
deepEqual(args, ['x']);
deepEqual(args, ['x', null]);
});
location.replace('http://example.com#route-event/x');
Backbone.history.checkUrl();
@@ -729,4 +734,47 @@
Backbone.history.navigate('path?query#hash', true);
});
test('Do not decode the search params.', function() {
var Router = Backbone.Router.extend({
routes: {
path: function(params){
strictEqual(params, 'x=y%20z');
}
}
});
var router = new Router;
Backbone.history.navigate('path?x=y%20z', true);
});
test('Navigate to a hash url.', function() {
Backbone.history.stop();
Backbone.history = _.extend(new Backbone.History, {location: location});
Backbone.history.start({pushState: true});
var Router = Backbone.Router.extend({
routes: {
path: function(params) {
strictEqual(params, 'x=y');
}
}
});
var router = new Router;
location.replace('http://example.com/path?x=y#hash');
Backbone.history.checkUrl();
});
test('#navigate to a hash url.', function() {
Backbone.history.stop();
Backbone.history = _.extend(new Backbone.History, {location: location});
Backbone.history.start({pushState: true});
var Router = Backbone.Router.extend({
routes: {
path: function(params) {
strictEqual(params, 'x=y');
}
}
});
var router = new Router;
Backbone.history.navigate('path?x=y#hash', true);
});
})();