This repository has been archived by the owner on Jul 6, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
backbone-route-filter.js
126 lines (110 loc) · 3.59 KB
/
backbone-route-filter.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/**
* Backbone.Route filter
*
* Adds support for sync/async Backbone routes filters
*
* @author Maksim Horbachevsky
*/
(function(factory) {
if (typeof define === 'function' && define.amd) {
define(['backbone', 'underscore'], factory);
} else if (typeof exports === 'object') {
module.exports = factory(require('backbone'), require('underscore'));
} else {
factory(window.Backbone, window._);
}
})(function(Backbone, _) {
var extend = Backbone.Router.extend;
Backbone.Router.extend = function() {
var child = extend.apply(this, arguments),
childProto = child.prototype,
parentProto = this.prototype;
_.each(['before', 'after'], function(filter) {
_.defaults(childProto[filter], parentProto[filter]);
});
return child;
};
_.extend(Backbone.Router.prototype, {
/**
* Override default route fn to call before/after filters
*
* @param {String} route
* @param {String} name
* @param {Function} [callback]
* @return {*}
*/
route: function(route, name, callback) {
if (!_.isRegExp(route)) route = this._routeToRegExp(route);
if (_.isFunction(name)) {
callback = name;
name = '';
}
if (!callback) callback = this[name];
var router = this;
Backbone.history.route(route, function(fragment) {
var args = router._extractParameters(route, fragment);
runFilters(router, router.before, fragment, args, function() {
if (router.execute(callback, args, name) !== false) {
router.trigger.apply(router, ['route:' + name].concat(args));
router.trigger('route', name, args);
Backbone.history.trigger('route', router, name, args);
runFilters(router, router.after, fragment, args, _.identity);
}
});
});
return this;
}
});
/**
* Running all filters that matches current fragment
*
* @param router {Router} router instance reference
* @param filters {Object} all available filters
* @param fragment {String} current fragment
* @param args {Array} fragment arguments
* @param callback {Function}
*/
function runFilters(router, filters, fragment, args, callback) {
var chain = _.filter(filters, function(callback, filter) {
filter = _.isRegExp(filter) ? filter : router._routeToRegExp(filter);
return filter.test(fragment);
});
run(chain, router, fragment, args, callback);
}
/**
* Recursive function to run through filters chain supporting both async calls via `next` or regular
* return-value based chain
*
* @param chain {Array} filter calls chain
* @param router {Router} router instance reference
* @param fragment {String} current fragment
* @param args {Array} fragment arguments
* @param callback {Function}
*/
function run(chain, router, fragment, args, callback) {
// When filters chain is finished - calling `done` callback
if (!chain.length) {
callback.call(router);
return;
}
var current = chain[0],
tail = _.tail(chain),
next = function() {
run(tail, router, fragment, args, callback);
};
if (_.isString(current)) {
current = router[current];
}
if (current.length === 3) {
// Filter expects `next` for async - ignoring return value
// and waiting for `next` to be called
current.apply(router, [fragment, args, next]);
} else {
// Using regular filter with `false` return value that stops
// filters execution
if (current.apply(router, [fragment, args]) !== false) {
next();
}
}
}
});