Built motion from commit 42b6453.|2.0.28
[motion2.git] / apidoc / vendor / path-to-regexp / index.js
1 var isArray = Array.isArray || function (arr) {
2   return Object.prototype.toString.call(arr) == '[object Array]';
3 };
4
5 /**
6  * Expose `pathToRegexp`.
7  */
8 // module.exports = pathToRegexp
9
10 /**
11  * The main path matching regexp utility.
12  *
13  * @type {RegExp}
14  */
15 var PATH_REGEXP = new RegExp([
16   // Match escaped characters that would otherwise appear in future matches.
17   // This allows the user to escape special characters that won't transform.
18   '(\\\\.)',
19   // Match Express-style parameters and un-named parameters with a prefix
20   // and optional suffixes. Matches appear as:
21   //
22   // "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?"]
23   // "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined]
24   '([\\/.])?(?:\\:(\\w+)(?:\\(((?:\\\\.|[^)])*)\\))?|\\(((?:\\\\.|[^)])*)\\))([+*?])?',
25   // Match regexp special characters that are always escaped.
26   '([.+*?=^!:${}()[\\]|\\/])'
27 ].join('|'), 'g');
28
29 /**
30  * Escape the capturing group by escaping special characters and meaning.
31  *
32  * @param  {String} group
33  * @return {String}
34  */
35 function escapeGroup (group) {
36   return group.replace(/([=!:$\/()])/g, '\\$1');
37 }
38
39 /**
40  * Attach the keys as a property of the regexp.
41  *
42  * @param  {RegExp} re
43  * @param  {Array}  keys
44  * @return {RegExp}
45  */
46 function attachKeys (re, keys) {
47   re.keys = keys;
48   return re;
49 }
50
51 /**
52  * Get the flags for a regexp from the options.
53  *
54  * @param  {Object} options
55  * @return {String}
56  */
57 function flags (options) {
58   return options.sensitive ? '' : 'i';
59 }
60
61 /**
62  * Pull out keys from a regexp.
63  *
64  * @param  {RegExp} path
65  * @param  {Array}  keys
66  * @return {RegExp}
67  */
68 function regexpToRegexp (path, keys) {
69   // Use a negative lookahead to match only capturing groups.
70   var groups = path.source.match(/\((?!\?)/g);
71
72   if (groups) {
73     for (var i = 0; i < groups.length; i++) {
74       keys.push({
75         name:      i,
76         delimiter: null,
77         optional:  false,
78         repeat:    false
79       });
80     }
81   }
82
83   return attachKeys(path, keys);
84 }
85
86 /**
87  * Transform an array into a regexp.
88  *
89  * @param  {Array}  path
90  * @param  {Array}  keys
91  * @param  {Object} options
92  * @return {RegExp}
93  */
94 function arrayToRegexp (path, keys, options) {
95   var parts = [];
96
97   for (var i = 0; i < path.length; i++) {
98     parts.push(pathToRegexp(path[i], keys, options).source);
99   }
100
101   var regexp = new RegExp('(?:' + parts.join('|') + ')', flags(options));
102   return attachKeys(regexp, keys);
103 }
104
105 /**
106  * Replace the specific tags with regexp strings.
107  *
108  * @param  {String} path
109  * @param  {Array}  keys
110  * @return {String}
111  */
112 function replacePath (path, keys) {
113   var index = 0;
114
115   function replace (_, escaped, prefix, key, capture, group, suffix, escape) {
116     if (escaped) {
117       return escaped;
118     }
119
120     if (escape) {
121       return '\\' + escape;
122     }
123
124     var repeat   = suffix === '+' || suffix === '*';
125     var optional = suffix === '?' || suffix === '*';
126
127     keys.push({
128       name:      key || index++,
129       delimiter: prefix || '/',
130       optional:  optional,
131       repeat:    repeat
132     });
133
134     prefix = prefix ? ('\\' + prefix) : '';
135     capture = escapeGroup(capture || group || '[^' + (prefix || '\\/') + ']+?');
136
137     if (repeat) {
138       capture = capture + '(?:' + prefix + capture + ')*';
139     }
140
141     if (optional) {
142       return '(?:' + prefix + '(' + capture + '))?';
143     }
144
145     // Basic parameter support.
146     return prefix + '(' + capture + ')';
147   }
148
149   return path.replace(PATH_REGEXP, replace);
150 }
151
152 /**
153  * Normalize the given path string, returning a regular expression.
154  *
155  * An empty array can be passed in for the keys, which will hold the
156  * placeholder key descriptions. For example, using `/user/:id`, `keys` will
157  * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`.
158  *
159  * @param  {(String|RegExp|Array)} path
160  * @param  {Array}                 [keys]
161  * @param  {Object}                [options]
162  * @return {RegExp}
163  */
164 function pathToRegexp (path, keys, options) {
165   keys = keys || [];
166
167   if (!isArray(keys)) {
168     options = keys;
169     keys = [];
170   } else if (!options) {
171     options = {};
172   }
173
174   if (path instanceof RegExp) {
175     return regexpToRegexp(path, keys, options);
176   }
177
178   if (isArray(path)) {
179     return arrayToRegexp(path, keys, options);
180   }
181
182   var strict = options.strict;
183   var end = options.end !== false;
184   var route = replacePath(path, keys);
185   var endsWithSlash = path.charAt(path.length - 1) === '/';
186
187   // In non-strict mode we allow a slash at the end of match. If the path to
188   // match already ends with a slash, we remove it for consistency. The slash
189   // is valid at the end of a path match, not in the middle. This is important
190   // in non-ending mode, where "/test/" shouldn't match "/test//route".
191   if (!strict) {
192     route = (endsWithSlash ? route.slice(0, -2) : route) + '(?:\\/(?=$))?';
193   }
194
195   if (end) {
196     route += '$';
197   } else {
198     // In non-ending mode, we need the capturing groups to match as much as
199     // possible by using a positive lookahead to the end or next path segment.
200     route += strict && endsWithSlash ? '' : '(?=\\/|$)';
201   }
202
203   return attachKeys(new RegExp('^' + route, flags(options)), keys);
204 }