730 lines
21 KiB
JavaScript
730 lines
21 KiB
JavaScript
(function() {
|
|
|
|
var router = null;
|
|
var location = null;
|
|
var lastRoute = null;
|
|
var lastArgs = [];
|
|
|
|
function onRoute(router, route, args) {
|
|
lastRoute = route;
|
|
lastArgs = args;
|
|
}
|
|
|
|
var Location = function(href) {
|
|
this.replace(href);
|
|
};
|
|
|
|
_.extend(Location.prototype, {
|
|
|
|
replace: function(href) {
|
|
_.extend(this, _.pick($('<a></a>', {href: href})[0],
|
|
'href',
|
|
'hash',
|
|
'host',
|
|
'search',
|
|
'fragment',
|
|
'pathname',
|
|
'protocol'
|
|
));
|
|
// In IE, anchor.pathname does not contain a leading slash though
|
|
// window.location.pathname does.
|
|
if (!/^\//.test(this.pathname)) this.pathname = '/' + this.pathname;
|
|
},
|
|
|
|
toString: function() {
|
|
return this.href;
|
|
}
|
|
|
|
});
|
|
|
|
module("Backbone.Router", {
|
|
|
|
setup: function() {
|
|
location = new Location('http://example.com');
|
|
Backbone.history = _.extend(new Backbone.History, {location: location});
|
|
router = new Router({testing: 101});
|
|
Backbone.history.interval = 9;
|
|
Backbone.history.start({pushState: false});
|
|
lastRoute = null;
|
|
lastArgs = [];
|
|
Backbone.history.on('route', onRoute);
|
|
},
|
|
|
|
teardown: function() {
|
|
Backbone.history.stop();
|
|
Backbone.history.off('route', onRoute);
|
|
}
|
|
|
|
});
|
|
|
|
var ExternalObject = {
|
|
value: 'unset',
|
|
|
|
routingFunction: function(value) {
|
|
this.value = value;
|
|
}
|
|
};
|
|
_.bindAll(ExternalObject);
|
|
|
|
var Router = Backbone.Router.extend({
|
|
|
|
count: 0,
|
|
|
|
routes: {
|
|
"noCallback": "noCallback",
|
|
"counter": "counter",
|
|
"search/:query": "search",
|
|
"search/:query/p:page": "search",
|
|
"charñ": "charUTF",
|
|
"char%C3%B1": "charEscaped",
|
|
"contacts": "contacts",
|
|
"contacts/new": "newContact",
|
|
"contacts/:id": "loadContact",
|
|
"route-event/:arg": "routeEvent",
|
|
"optional(/:item)": "optionalItem",
|
|
"named/optional/(y:z)": "namedOptional",
|
|
"splat/*args/end": "splat",
|
|
":repo/compare/*from...*to": "github",
|
|
"decode/:named/*splat": "decode",
|
|
"*first/complex-*part/*rest": "complex",
|
|
":entity?*args": "query",
|
|
"function/:value": ExternalObject.routingFunction,
|
|
"*anything": "anything"
|
|
},
|
|
|
|
initialize : function(options) {
|
|
this.testing = options.testing;
|
|
this.route('implicit', 'implicit');
|
|
},
|
|
|
|
counter: function() {
|
|
this.count++;
|
|
},
|
|
|
|
implicit: function() {
|
|
this.count++;
|
|
},
|
|
|
|
search: function(query, page) {
|
|
this.query = query;
|
|
this.page = page;
|
|
},
|
|
|
|
charUTF: function() {
|
|
this.charType = 'UTF';
|
|
},
|
|
|
|
charEscaped: function() {
|
|
this.charType = 'escaped';
|
|
},
|
|
|
|
contacts: function(){
|
|
this.contact = 'index';
|
|
},
|
|
|
|
newContact: function(){
|
|
this.contact = 'new';
|
|
},
|
|
|
|
loadContact: function(){
|
|
this.contact = 'load';
|
|
},
|
|
|
|
optionalItem: function(arg){
|
|
this.arg = arg != void 0 ? arg : null;
|
|
},
|
|
|
|
splat: function(args) {
|
|
this.args = args;
|
|
},
|
|
|
|
github: function(repo, from, to) {
|
|
this.repo = repo;
|
|
this.from = from;
|
|
this.to = to;
|
|
},
|
|
|
|
complex: function(first, part, rest) {
|
|
this.first = first;
|
|
this.part = part;
|
|
this.rest = rest;
|
|
},
|
|
|
|
query: function(entity, args) {
|
|
this.entity = entity;
|
|
this.queryArgs = args;
|
|
},
|
|
|
|
anything: function(whatever) {
|
|
this.anything = whatever;
|
|
},
|
|
|
|
namedOptional: function(z) {
|
|
this.z = z;
|
|
},
|
|
|
|
decode: function(named, path) {
|
|
this.named = named;
|
|
this.path = path;
|
|
},
|
|
|
|
routeEvent: function(arg) {
|
|
}
|
|
|
|
});
|
|
|
|
test("initialize", 1, function() {
|
|
equal(router.testing, 101);
|
|
});
|
|
|
|
test("routes (simple)", 4, function() {
|
|
location.replace('http://example.com#search/news');
|
|
Backbone.history.checkUrl();
|
|
equal(router.query, 'news');
|
|
equal(router.page, void 0);
|
|
equal(lastRoute, 'search');
|
|
equal(lastArgs[0], 'news');
|
|
});
|
|
|
|
test("routes (simple, but unicode)", 4, function() {
|
|
location.replace('http://example.com#search/тест');
|
|
Backbone.history.checkUrl();
|
|
equal(router.query, "тест");
|
|
equal(router.page, void 0);
|
|
equal(lastRoute, 'search');
|
|
equal(lastArgs[0], "тест");
|
|
});
|
|
|
|
test("routes (two part)", 2, function() {
|
|
location.replace('http://example.com#search/nyc/p10');
|
|
Backbone.history.checkUrl();
|
|
equal(router.query, 'nyc');
|
|
equal(router.page, '10');
|
|
});
|
|
|
|
test("routes via navigate", 2, function() {
|
|
Backbone.history.navigate('search/manhattan/p20', {trigger: true});
|
|
equal(router.query, 'manhattan');
|
|
equal(router.page, '20');
|
|
});
|
|
|
|
test("routes via navigate for backwards-compatibility", 2, function() {
|
|
Backbone.history.navigate('search/manhattan/p20', true);
|
|
equal(router.query, 'manhattan');
|
|
equal(router.page, '20');
|
|
});
|
|
|
|
test("reports matched route via nagivate", 1, function() {
|
|
ok(Backbone.history.navigate('search/manhattan/p20', true));
|
|
});
|
|
|
|
test("route precedence via navigate", 6, function(){
|
|
// check both 0.9.x and backwards-compatibility options
|
|
_.each([ { trigger: true }, true ], function( options ){
|
|
Backbone.history.navigate('contacts', options);
|
|
equal(router.contact, 'index');
|
|
Backbone.history.navigate('contacts/new', options);
|
|
equal(router.contact, 'new');
|
|
Backbone.history.navigate('contacts/foo', options);
|
|
equal(router.contact, 'load');
|
|
});
|
|
});
|
|
|
|
test("loadUrl is not called for identical routes.", 0, function() {
|
|
Backbone.history.loadUrl = function(){ ok(false); };
|
|
location.replace('http://example.com#route');
|
|
Backbone.history.navigate('route');
|
|
Backbone.history.navigate('/route');
|
|
Backbone.history.navigate('/route');
|
|
});
|
|
|
|
test("use implicit callback if none provided", 1, function() {
|
|
router.count = 0;
|
|
router.navigate('implicit', {trigger: true});
|
|
equal(router.count, 1);
|
|
});
|
|
|
|
test("routes via navigate with {replace: true}", 1, function() {
|
|
location.replace('http://example.com#start_here');
|
|
Backbone.history.checkUrl();
|
|
location.replace = function(href) {
|
|
strictEqual(href, new Location('http://example.com#end_here').href);
|
|
};
|
|
Backbone.history.navigate('end_here', {replace: true});
|
|
});
|
|
|
|
test("routes (splats)", 1, function() {
|
|
location.replace('http://example.com#splat/long-list/of/splatted_99args/end');
|
|
Backbone.history.checkUrl();
|
|
equal(router.args, 'long-list/of/splatted_99args');
|
|
});
|
|
|
|
test("routes (github)", 3, function() {
|
|
location.replace('http://example.com#backbone/compare/1.0...braddunbar:with/slash');
|
|
Backbone.history.checkUrl();
|
|
equal(router.repo, 'backbone');
|
|
equal(router.from, '1.0');
|
|
equal(router.to, 'braddunbar:with/slash');
|
|
});
|
|
|
|
test("routes (optional)", 2, function() {
|
|
location.replace('http://example.com#optional');
|
|
Backbone.history.checkUrl();
|
|
ok(!router.arg);
|
|
location.replace('http://example.com#optional/thing');
|
|
Backbone.history.checkUrl();
|
|
equal(router.arg, 'thing');
|
|
});
|
|
|
|
test("routes (complex)", 3, function() {
|
|
location.replace('http://example.com#one/two/three/complex-part/four/five/six/seven');
|
|
Backbone.history.checkUrl();
|
|
equal(router.first, 'one/two/three');
|
|
equal(router.part, 'part');
|
|
equal(router.rest, 'four/five/six/seven');
|
|
});
|
|
|
|
test("routes (query)", 5, function() {
|
|
location.replace('http://example.com#mandel?a=b&c=d');
|
|
Backbone.history.checkUrl();
|
|
equal(router.entity, 'mandel');
|
|
equal(router.queryArgs, 'a=b&c=d');
|
|
equal(lastRoute, 'query');
|
|
equal(lastArgs[0], 'mandel');
|
|
equal(lastArgs[1], 'a=b&c=d');
|
|
});
|
|
|
|
test("routes (anything)", 1, function() {
|
|
location.replace('http://example.com#doesnt-match-a-route');
|
|
Backbone.history.checkUrl();
|
|
equal(router.anything, 'doesnt-match-a-route');
|
|
});
|
|
|
|
test("routes (function)", 3, function() {
|
|
router.on('route', function(name) {
|
|
ok(name === '');
|
|
});
|
|
equal(ExternalObject.value, 'unset');
|
|
location.replace('http://example.com#function/set');
|
|
Backbone.history.checkUrl();
|
|
equal(ExternalObject.value, 'set');
|
|
});
|
|
|
|
test("Decode named parameters, not splats.", 2, function() {
|
|
location.replace('http://example.com#decode/a%2Fb/c%2Fd/e');
|
|
Backbone.history.checkUrl();
|
|
strictEqual(router.named, 'a/b');
|
|
strictEqual(router.path, 'c/d/e');
|
|
});
|
|
|
|
test("fires event when router doesn't have callback on it", 1, function() {
|
|
router.on("route:noCallback", function(){ ok(true); });
|
|
location.replace('http://example.com#noCallback');
|
|
Backbone.history.checkUrl();
|
|
});
|
|
|
|
test("#933, #908 - leading slash", 2, function() {
|
|
location.replace('http://example.com/root/foo');
|
|
|
|
Backbone.history.stop();
|
|
Backbone.history = _.extend(new Backbone.History, {location: location});
|
|
Backbone.history.start({root: '/root', hashChange: false, silent: true});
|
|
strictEqual(Backbone.history.getFragment(), 'foo');
|
|
|
|
Backbone.history.stop();
|
|
Backbone.history = _.extend(new Backbone.History, {location: location});
|
|
Backbone.history.start({root: '/root/', hashChange: false, silent: true});
|
|
strictEqual(Backbone.history.getFragment(), 'foo');
|
|
});
|
|
|
|
test("#1003 - History is started before navigate is called", 1, function() {
|
|
Backbone.history.stop();
|
|
Backbone.history.navigate = function(){ ok(Backbone.History.started); };
|
|
Backbone.history.start();
|
|
// If this is not an old IE navigate will not be called.
|
|
if (!Backbone.history.iframe) ok(true);
|
|
});
|
|
|
|
test("#967 - Route callback gets passed encoded values.", 3, function() {
|
|
var route = 'has%2Fslash/complex-has%23hash/has%20space';
|
|
Backbone.history.navigate(route, {trigger: true});
|
|
strictEqual(router.first, 'has/slash');
|
|
strictEqual(router.part, 'has#hash');
|
|
strictEqual(router.rest, 'has space');
|
|
});
|
|
|
|
test("correctly handles URLs with % (#868)", 3, function() {
|
|
location.replace('http://example.com#search/fat%3A1.5%25');
|
|
Backbone.history.checkUrl();
|
|
location.replace('http://example.com#search/fat');
|
|
Backbone.history.checkUrl();
|
|
equal(router.query, 'fat');
|
|
equal(router.page, void 0);
|
|
equal(lastRoute, 'search');
|
|
});
|
|
|
|
test("#2666 - Hashes with UTF8 in them.", 2, function() {
|
|
Backbone.history.navigate('charñ', {trigger: true});
|
|
equal(router.charType, 'UTF');
|
|
Backbone.history.navigate('char%C3%B1', {trigger: true});
|
|
equal(router.charType, 'escaped');
|
|
});
|
|
|
|
test("#1185 - Use pathname when hashChange is not wanted.", 1, function() {
|
|
Backbone.history.stop();
|
|
location.replace('http://example.com/path/name#hash');
|
|
Backbone.history = _.extend(new Backbone.History, {location: location});
|
|
Backbone.history.start({hashChange: false});
|
|
var fragment = Backbone.history.getFragment();
|
|
strictEqual(fragment, location.pathname.replace(/^\//, ''));
|
|
});
|
|
|
|
test("#1206 - Strip leading slash before location.assign.", 1, function() {
|
|
Backbone.history.stop();
|
|
location.replace('http://example.com/root/');
|
|
Backbone.history = _.extend(new Backbone.History, {location: location});
|
|
Backbone.history.start({hashChange: false, root: '/root/'});
|
|
location.assign = function(pathname) {
|
|
strictEqual(pathname, '/root/fragment');
|
|
};
|
|
Backbone.history.navigate('/fragment');
|
|
});
|
|
|
|
test("#1387 - Root fragment without trailing slash.", 1, function() {
|
|
Backbone.history.stop();
|
|
location.replace('http://example.com/root');
|
|
Backbone.history = _.extend(new Backbone.History, {location: location});
|
|
Backbone.history.start({hashChange: false, root: '/root/', silent: true});
|
|
strictEqual(Backbone.history.getFragment(), '');
|
|
});
|
|
|
|
test("#1366 - History does not prepend root to fragment.", 2, function() {
|
|
Backbone.history.stop();
|
|
location.replace('http://example.com/root/');
|
|
Backbone.history = _.extend(new Backbone.History, {
|
|
location: location,
|
|
history: {
|
|
pushState: function(state, title, url) {
|
|
strictEqual(url, '/root/x');
|
|
}
|
|
}
|
|
});
|
|
Backbone.history.start({
|
|
root: '/root/',
|
|
pushState: true,
|
|
hashChange: false
|
|
});
|
|
Backbone.history.navigate('x');
|
|
strictEqual(Backbone.history.fragment, 'x');
|
|
});
|
|
|
|
test("Normalize root.", 1, function() {
|
|
Backbone.history.stop();
|
|
location.replace('http://example.com/root');
|
|
Backbone.history = _.extend(new Backbone.History, {
|
|
location: location,
|
|
history: {
|
|
pushState: function(state, title, url) {
|
|
strictEqual(url, '/root/fragment');
|
|
}
|
|
}
|
|
});
|
|
Backbone.history.start({
|
|
pushState: true,
|
|
root: '/root',
|
|
hashChange: false
|
|
});
|
|
Backbone.history.navigate('fragment');
|
|
});
|
|
|
|
test("Normalize root.", 1, function() {
|
|
Backbone.history.stop();
|
|
location.replace('http://example.com/root#fragment');
|
|
Backbone.history = _.extend(new Backbone.History, {
|
|
location: location,
|
|
history: {
|
|
pushState: function(state, title, url) {},
|
|
replaceState: function(state, title, url) {
|
|
strictEqual(url, '/root/fragment');
|
|
}
|
|
}
|
|
});
|
|
Backbone.history.start({
|
|
pushState: true,
|
|
root: '/root'
|
|
});
|
|
});
|
|
|
|
test("Normalize root.", 1, function() {
|
|
Backbone.history.stop();
|
|
location.replace('http://example.com/root');
|
|
Backbone.history = _.extend(new Backbone.History, {location: location});
|
|
Backbone.history.loadUrl = function() { ok(true); };
|
|
Backbone.history.start({
|
|
pushState: true,
|
|
root: '/root'
|
|
});
|
|
});
|
|
|
|
test("Normalize root - leading slash.", 1, function() {
|
|
Backbone.history.stop();
|
|
location.replace('http://example.com/root');
|
|
Backbone.history = _.extend(new Backbone.History, {
|
|
location: location,
|
|
history: {
|
|
pushState: function(){},
|
|
replaceState: function(){}
|
|
}
|
|
});
|
|
Backbone.history.start({root: 'root'});
|
|
strictEqual(Backbone.history.root, '/root/');
|
|
});
|
|
|
|
test("Transition from hashChange to pushState.", 1, function() {
|
|
Backbone.history.stop();
|
|
location.replace('http://example.com/root#x/y');
|
|
Backbone.history = _.extend(new Backbone.History, {
|
|
location: location,
|
|
history: {
|
|
pushState: function(){},
|
|
replaceState: function(state, title, url){
|
|
strictEqual(url, '/root/x/y');
|
|
}
|
|
}
|
|
});
|
|
Backbone.history.start({
|
|
root: 'root',
|
|
pushState: true
|
|
});
|
|
});
|
|
|
|
test("#1619: Router: Normalize empty root", 1, function() {
|
|
Backbone.history.stop();
|
|
location.replace('http://example.com/');
|
|
Backbone.history = _.extend(new Backbone.History, {
|
|
location: location,
|
|
history: {
|
|
pushState: function(){},
|
|
replaceState: function(){}
|
|
}
|
|
});
|
|
Backbone.history.start({root: ''});
|
|
strictEqual(Backbone.history.root, '/');
|
|
});
|
|
|
|
test("#1619: Router: nagivate with empty root", 1, function() {
|
|
Backbone.history.stop();
|
|
location.replace('http://example.com/');
|
|
Backbone.history = _.extend(new Backbone.History, {
|
|
location: location,
|
|
history: {
|
|
pushState: function(state, title, url) {
|
|
strictEqual(url, '/fragment');
|
|
}
|
|
}
|
|
});
|
|
Backbone.history.start({
|
|
pushState: true,
|
|
root: '',
|
|
hashChange: false
|
|
});
|
|
Backbone.history.navigate('fragment');
|
|
});
|
|
|
|
test("Transition from pushState to hashChange.", 1, function() {
|
|
Backbone.history.stop();
|
|
location.replace('http://example.com/root/x/y?a=b');
|
|
location.replace = function(url) {
|
|
strictEqual(url, '/root/?a=b#x/y');
|
|
};
|
|
Backbone.history = _.extend(new Backbone.History, {
|
|
location: location,
|
|
history: {
|
|
pushState: null,
|
|
replaceState: null
|
|
}
|
|
});
|
|
Backbone.history.start({
|
|
root: 'root',
|
|
pushState: true
|
|
});
|
|
});
|
|
|
|
test("#1695 - hashChange to pushState with search.", 1, function() {
|
|
Backbone.history.stop();
|
|
location.replace('http://example.com/root?a=b#x/y');
|
|
Backbone.history = _.extend(new Backbone.History, {
|
|
location: location,
|
|
history: {
|
|
pushState: function(){},
|
|
replaceState: function(state, title, url){
|
|
strictEqual(url, '/root/x/y?a=b');
|
|
}
|
|
}
|
|
});
|
|
Backbone.history.start({
|
|
root: 'root',
|
|
pushState: true
|
|
});
|
|
});
|
|
|
|
test("#1746 - Router allows empty route.", 1, function() {
|
|
var Router = Backbone.Router.extend({
|
|
routes: {'': 'empty'},
|
|
empty: function(){},
|
|
route: function(route){
|
|
strictEqual(route, '');
|
|
}
|
|
});
|
|
new Router;
|
|
});
|
|
|
|
test("#1794 - Trailing space in fragments.", 1, function() {
|
|
var history = new Backbone.History;
|
|
strictEqual(history.getFragment('fragment '), 'fragment');
|
|
});
|
|
|
|
test("#1820 - Leading slash and trailing space.", 1, function() {
|
|
var history = new Backbone.History;
|
|
strictEqual(history.getFragment('/fragment '), 'fragment');
|
|
});
|
|
|
|
test("#1980 - Optional parameters.", 2, function() {
|
|
location.replace('http://example.com#named/optional/y');
|
|
Backbone.history.checkUrl();
|
|
strictEqual(router.z, undefined);
|
|
location.replace('http://example.com#named/optional/y123');
|
|
Backbone.history.checkUrl();
|
|
strictEqual(router.z, '123');
|
|
});
|
|
|
|
test("#2062 - Trigger 'route' event on router instance.", 2, function() {
|
|
router.on('route', function(name, args) {
|
|
strictEqual(name, 'routeEvent');
|
|
deepEqual(args, ['x']);
|
|
});
|
|
location.replace('http://example.com#route-event/x');
|
|
Backbone.history.checkUrl();
|
|
});
|
|
|
|
test("#2255 - Extend routes by making routes a function.", 1, function() {
|
|
var RouterBase = Backbone.Router.extend({
|
|
routes: function() {
|
|
return {
|
|
home: "root",
|
|
index: "index.html"
|
|
};
|
|
}
|
|
});
|
|
|
|
var RouterExtended = RouterBase.extend({
|
|
routes: function() {
|
|
var _super = RouterExtended.__super__.routes;
|
|
return _.extend(_super(),
|
|
{ show: "show",
|
|
search: "search" });
|
|
}
|
|
});
|
|
|
|
var router = new RouterExtended();
|
|
deepEqual({home: "root", index: "index.html", show: "show", search: "search"}, router.routes);
|
|
});
|
|
|
|
test("#2538 - hashChange to pushState only if both requested.", 0, function() {
|
|
Backbone.history.stop();
|
|
location.replace('http://example.com/root?a=b#x/y');
|
|
Backbone.history = _.extend(new Backbone.History, {
|
|
location: location,
|
|
history: {
|
|
pushState: function(){},
|
|
replaceState: function(){ ok(false); }
|
|
}
|
|
});
|
|
Backbone.history.start({
|
|
root: 'root',
|
|
pushState: true,
|
|
hashChange: false
|
|
});
|
|
});
|
|
|
|
test('No hash fallback.', 0, function() {
|
|
Backbone.history.stop();
|
|
Backbone.history = _.extend(new Backbone.History, {
|
|
location: location,
|
|
history: {
|
|
pushState: function(){},
|
|
replaceState: function(){}
|
|
}
|
|
});
|
|
|
|
var Router = Backbone.Router.extend({
|
|
routes: {
|
|
hash: function() { ok(false); }
|
|
}
|
|
});
|
|
var router = new Router;
|
|
|
|
location.replace('http://example.com/');
|
|
Backbone.history.start({
|
|
pushState: true,
|
|
hashChange: false
|
|
});
|
|
location.replace('http://example.com/nomatch#hash');
|
|
Backbone.history.checkUrl();
|
|
});
|
|
|
|
test('#2656 - No trailing slash on root.', 1, function() {
|
|
Backbone.history.stop();
|
|
Backbone.history = _.extend(new Backbone.History, {
|
|
location: location,
|
|
history: {
|
|
pushState: function(state, title, url){
|
|
strictEqual(url, '/root');
|
|
}
|
|
}
|
|
});
|
|
location.replace('http://example.com/root/path');
|
|
Backbone.history.start({pushState: true, root: 'root'});
|
|
Backbone.history.navigate('');
|
|
});
|
|
|
|
test('#2656 - No trailing slash on root.', 1, function() {
|
|
Backbone.history.stop();
|
|
Backbone.history = _.extend(new Backbone.History, {
|
|
location: location,
|
|
history: {
|
|
pushState: function(state, title, url) {
|
|
strictEqual(url, '/');
|
|
}
|
|
}
|
|
});
|
|
location.replace('http://example.com/path');
|
|
Backbone.history.start({pushState: true});
|
|
Backbone.history.navigate('');
|
|
});
|
|
|
|
test('#2765 - Fragment matching sans query/hash.', 2, function() {
|
|
Backbone.history.stop();
|
|
Backbone.history = _.extend(new Backbone.History, {
|
|
location: location,
|
|
history: {
|
|
pushState: function(state, title, url) {
|
|
strictEqual(url, '/path?query#hash');
|
|
}
|
|
}
|
|
});
|
|
|
|
var Router = Backbone.Router.extend({
|
|
routes: {
|
|
path: function() { ok(true); }
|
|
}
|
|
});
|
|
var router = new Router;
|
|
|
|
location.replace('http://example.com/');
|
|
Backbone.history.start({pushState: true});
|
|
Backbone.history.navigate('path?query#hash', true);
|
|
});
|
|
|
|
})();
|