Skip to content

Commit

Permalink
Merge pull request #4281 from CentreForDigitalHumanities/history-notf…
Browse files Browse the repository at this point in the history
…ound-event

History notfound event
  • Loading branch information
jgonggrijp committed Aug 10, 2023
2 parents 5df475d + f434aa9 commit 24c9855
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 5 deletions.
17 changes: 14 additions & 3 deletions backbone.js
Expand Up @@ -1985,7 +1985,10 @@
current = this.getHash(this.iframe.contentWindow);
}

if (current === this.fragment) return false;
if (current === this.fragment) {
if (!this.matchRoot()) return this.notfound();
return false;
}
if (this.iframe) this.navigate(current);
this.loadUrl();
},
Expand All @@ -1995,14 +1998,22 @@
// returns `false`.
loadUrl: function(fragment) {
// If the root doesn't match, no routes can match either.
if (!this.matchRoot()) return false;
if (!this.matchRoot()) return this.notfound();
fragment = this.fragment = this.getFragment(fragment);
return _.some(this.handlers, function(handler) {
if (handler.route.test(fragment)) {
handler.callback(fragment);
return true;
}
});
}) || this.notfound();
},

// When no route could be matched, this method is called internally to
// trigger the `'notfound'` event. It returns `false` so that it can be used
// in tail position.
notfound: function() {
this.trigger('notfound');
return false;
},

// Save a fragment into the hash history, or replace the URL state if the
Expand Down
10 changes: 8 additions & 2 deletions index.html
Expand Up @@ -1040,6 +1040,7 @@ <h2 id="Events">Backbone.Events</h2>
<li><b>"route:[name]"</b> (params) &mdash; Fired by the router when a specific route is matched.</li>
<li><b>"route"</b> (route, params) &mdash; Fired by the router when <i>any</i> route has been matched.</li>
<li><b>"route"</b> (router, route, params) &mdash; Fired by history when <i>any</i> route has been matched.</li>
<li><b>"notfound"</b> () &mdash; Fired by history when <i>no</i> route could be matched.</li>
<li><b>"all"</b> &mdash; this special event fires for <i>any</i> triggered event, passing the event name as the first argument followed by all trigger arguments.</li>
</ul>

Expand Down Expand Up @@ -2726,6 +2727,9 @@ <h2 id="History">Backbone.history</h2>
<p>
<b>History</b> serves as a global router (per frame) to handle <tt>hashchange</tt>
events or <tt>pushState</tt>, match the appropriate route, and trigger callbacks.
It forwards the <tt>"route"</tt> and <tt>"route[name]"</tt> events of the
matching router, or <tt>"notfound"</tt> when no route in any router
matches the current URL.
You shouldn't ever have to create one of these yourself since <tt>Backbone.history</tt>
already contains one.
</p>
Expand Down Expand Up @@ -2782,8 +2786,10 @@ <h2 id="History">Backbone.history</h2>

<p>
When called, if a route succeeds with a match for the current URL,
<tt>Backbone.history.start()</tt> returns <tt>true</tt>. If no defined
route matches the current URL, it returns <tt>false</tt>.
<tt>Backbone.history.start()</tt> returns <tt>true</tt> and
the <tt>"route"</tt> and <tt>"route[name]"</tt> events are triggered. If
no defined route matches the current URL, it returns <tt>false</tt>
and <tt>"notfound"</tt> is triggered instead.
</p>

<p>
Expand Down
85 changes: 85 additions & 0 deletions test/router.js
Expand Up @@ -1110,4 +1110,89 @@
assert.strictEqual(location.hash, '#' + route);
});

QUnit.test('initial non-matching root triggers notfound event', function(assert) {
assert.expect(1);
location.replace('http://example.com/root#foo');
Backbone.history.stop();
Backbone.history = _.extend(new Backbone.History, {location: location});
Backbone.history.on('notfound', function() { assert.ok(true); });
var MyRouter = Backbone.Router.extend({
routes: {foo: function() { assert.ok(false); }}
});
var myRouter = new MyRouter;
Backbone.history.start({root: 'other'});
});

QUnit.test('later non-matching root triggers notfound event', function(assert) {
assert.expect(2);
location.replace('http://example.com/root#foo');
Backbone.history.stop();
Backbone.history = _.extend(new Backbone.History, {location: location});
Backbone.history.on('notfound', function() { assert.ok(true); });
var MyRouter = Backbone.Router.extend({
routes: {foo: function() { assert.ok(true); }}
});
var myRouter = new MyRouter;
Backbone.history.start({root: 'root'});
location.replace('http://example.com/other#foo');
Backbone.history.checkUrl();
});

QUnit.test('initial non-matching route triggers notfound event', function(assert) {
assert.expect(1);
location.replace('http://example.com/root#bar');
Backbone.history.stop();
Backbone.history = _.extend(new Backbone.History, {location: location});
Backbone.history.on('notfound', function() { assert.ok(true); });
var MyRouter = Backbone.Router.extend({
routes: {foo: function() { assert.ok(false); }}
});
var myRouter = new MyRouter;
Backbone.history.start({root: 'root'});
});

QUnit.test('later non-matching route triggers notfound event', function(assert) {
assert.expect(2);
location.replace('http://example.com/root#foo');
Backbone.history.stop();
Backbone.history = _.extend(new Backbone.History, {location: location});
Backbone.history.on('notfound', function() { assert.ok(true); });
var MyRouter = Backbone.Router.extend({
routes: {foo: function() { assert.ok(true); }}
});
var myRouter = new MyRouter;
Backbone.history.start({root: 'root'});
location.replace('http://example.com/other#bar');
Backbone.history.checkUrl();
});

QUnit.test('non-matching pushState route triggers notfound event', function(assert) {
assert.expect(2);
location.replace('http://example.com/root/foo');
Backbone.history.stop();
Backbone.history = _.extend(new Backbone.History, {location: location});
Backbone.history.on('notfound', function() { assert.ok(true); });
var MyRouter = Backbone.Router.extend({
routes: {foo: function() { assert.ok(true); }}
});
var myRouter = new MyRouter;
Backbone.history.start({root: 'root', pushState: true});
location.replace('http://example.com/other/bar');
Backbone.history.checkUrl();
});

QUnit.test('non-matching navigate triggers notfound event', function(assert) {
assert.expect(2);
location.replace('http://example.com/root#foo');
Backbone.history.stop();
Backbone.history = _.extend(new Backbone.History, {location: location});
Backbone.history.on('notfound', function() { assert.ok(true); });
var MyRouter = Backbone.Router.extend({
routes: {foo: function() { assert.ok(true); }}
});
var myRouter = new MyRouter;
Backbone.history.start({root: 'root'});
Backbone.history.navigate('http://example.com/other#bar', {trigger: true});
});

})(QUnit);

0 comments on commit 24c9855

Please sign in to comment.