-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
132 lines (100 loc) · 2.96 KB
/
index.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
127
128
129
130
131
/*
* DocURI: Rich document ids for CouchDB.
*
* Copyright (c) 2014 null2 GmbH Berlin
* Licensed under the MIT license.
*/
// type/id/subtype/index/version
var docuri = module.exports = exports = {};
// Cached regular expressions for matching named param parts and splatted parts
// of route strings.
// http://backbonejs.org/docs/backbone.html#section-158
var optionalParam = /\((.*?)\)/g;
var namedParam = /(\(\?)?:\w+/g;
var splatParam = /\*\w+/g;
var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g;
var paramKeys = /[*:]\w+/g;
// Convert a route string into a regular expression,
// with named regular expressions for named arguments.
// http://backbonejs.org/docs/backbone.html#section-165
function routeToRegExp(src) {
var keys = [], match;
while ( ( match = paramKeys.exec( src ) ) !== null )
{
keys.push( match[0] );
}
var route = src.replace(escapeRegExp, '\\$&')
.replace(optionalParam, '(?:$1)?')
.replace(namedParam, function(match, optional) {
return optional ? match : '([^/?]+)';
})
.replace(splatParam, '([^?]*?)');
keys = keys.reduce(function(memo, key) {
var value = '\\' + key;
memo[key] = new RegExp(value + '(\\/|\\)|\\(|$)');
return memo;
}, {});
return {
src: src,
exp: new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$'),
keys: keys
}
}
// Given a route and a DocURI return an object of extracted parameters.
// Unmatched DocURIs will be treated as false.
// http://backbonejs.org/docs/backbone.html#section-166
function extractParameters(route, fragment) {
var params = route.exp.exec(fragment);
if (!params) {
return false;
}
params = params.slice(1);
return Object.keys(route.keys).reduce(function(memo, key, i) {
var param = params[i];
if (param) {
if (key[0] === '*') {
param = param.split('/').map(decodeURIComponent);
} else {
param = decodeURIComponent(param);
}
memo[key.substr(1)] = param;
}
return memo;
}, {});
}
// Insert named parameters from object.
function insertParameters(route, obj) {
var str = route.src;
Object.keys(route.keys).forEach(function(key) {
var k = key.substr(1);
var value = obj[k] || '';
if (Array.isArray(value)) {
value = value.map(encodeURIComponent).join('/');
} else {
value = encodeURIComponent(value);
}
str = str.replace(route.keys[key], value + '$1');
});
// massage optional parameter
return str
.replace(/\(\/\)(\/|$)/g, '$1')
.replace(/[)(]/g, '');
}
docuri.route = function(route) {
route = routeToRegExp(route);
return function(source, target) {
source = source || {};
if (target) {
source = extractParameters(route, source);
Object.keys(target).forEach(function(key) {
source[key] = target[key];
});
}
if (typeof source === 'object') {
return insertParameters(route, source);
}
if (typeof source === 'string') {
return extractParameters(route, source);
}
};
};