mirror of
https://github.com/jashkenas/backbone.git
synced 2026-04-30 03:00:06 -04:00
40
backbone.js
40
backbone.js
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
})();
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user