source: Dev/trunk/src/node_modules/express/lib/utils.js @ 484

Last change on this file since 484 was 484, checked in by hendrikvanantwerpen, 11 years ago

Commit node_modules, to make checkouts and builds more deterministic.

File size: 6.3 KB
Line 
1
2/**
3 * Module dependencies.
4 */
5
6var mime = require('connect').mime
7  , crc32 = require('buffer-crc32');
8
9/**
10 * toString ref.
11 */
12
13var toString = {}.toString;
14
15/**
16 * Return ETag for `body`.
17 *
18 * @param {String|Buffer} body
19 * @return {String}
20 * @api private
21 */
22
23exports.etag = function(body){
24  return '"' + crc32.signed(body) + '"';
25};
26
27/**
28 * Make `locals()` bound to the given `obj`.
29 *
30 * This is used for `app.locals` and `res.locals`.
31 *
32 * @param {Object} obj
33 * @return {Function}
34 * @api private
35 */
36
37exports.locals = function(obj){
38  function locals(obj){
39    for (var key in obj) locals[key] = obj[key];
40    return obj;
41  };
42
43  return locals;
44};
45
46/**
47 * Check if `path` looks absolute.
48 *
49 * @param {String} path
50 * @return {Boolean}
51 * @api private
52 */
53
54exports.isAbsolute = function(path){
55  if ('/' == path[0]) return true;
56  if (':' == path[1] && '\\' == path[2]) return true;
57};
58
59/**
60 * Flatten the given `arr`.
61 *
62 * @param {Array} arr
63 * @return {Array}
64 * @api private
65 */
66
67exports.flatten = function(arr, ret){
68  var ret = ret || []
69    , len = arr.length;
70  for (var i = 0; i < len; ++i) {
71    if (Array.isArray(arr[i])) {
72      exports.flatten(arr[i], ret);
73    } else {
74      ret.push(arr[i]);
75    }
76  }
77  return ret;
78};
79
80/**
81 * Normalize the given `type`, for example "html" becomes "text/html".
82 *
83 * @param {String} type
84 * @return {Object}
85 * @api private
86 */
87
88exports.normalizeType = function(type){
89  return ~type.indexOf('/')
90    ? acceptParams(type)
91    : { value: mime.lookup(type), params: {} };
92};
93
94/**
95 * Normalize `types`, for example "html" becomes "text/html".
96 *
97 * @param {Array} types
98 * @return {Array}
99 * @api private
100 */
101
102exports.normalizeTypes = function(types){
103  var ret = [];
104
105  for (var i = 0; i < types.length; ++i) {
106    ret.push(exports.normalizeType(types[i]));
107  }
108
109  return ret;
110};
111
112/**
113 * Return the acceptable type in `types`, if any.
114 *
115 * @param {Array} types
116 * @param {String} str
117 * @return {String}
118 * @api private
119 */
120
121exports.acceptsArray = function(types, str){
122  // accept anything when Accept is not present
123  if (!str) return types[0];
124
125  // parse
126  var accepted = exports.parseAccept(str)
127    , normalized = exports.normalizeTypes(types)
128    , len = accepted.length;
129
130  for (var i = 0; i < len; ++i) {
131    for (var j = 0, jlen = types.length; j < jlen; ++j) {
132      if (exports.accept(normalized[j], accepted[i])) {
133        return types[j];
134      }
135    }
136  }
137};
138
139/**
140 * Check if `type(s)` are acceptable based on
141 * the given `str`.
142 *
143 * @param {String|Array} type(s)
144 * @param {String} str
145 * @return {Boolean|String}
146 * @api private
147 */
148
149exports.accepts = function(type, str){
150  if ('string' == typeof type) type = type.split(/ *, */);
151  return exports.acceptsArray(type, str);
152};
153
154/**
155 * Check if `type` array is acceptable for `other`.
156 *
157 * @param {Object} type
158 * @param {Object} other
159 * @return {Boolean}
160 * @api private
161 */
162
163exports.accept = function(type, other){
164  var t = type.value.split('/');
165  return (t[0] == other.type || '*' == other.type)
166    && (t[1] == other.subtype || '*' == other.subtype)
167    && paramsEqual(type.params, other.params);
168};
169
170/**
171 * Check if accept params are equal.
172 *
173 * @param {Object} a
174 * @param {Object} b
175 * @return {Boolean}
176 * @api private
177 */
178
179function paramsEqual(a, b){
180  return !Object.keys(a).some(function(k) {
181    return a[k] != b[k];
182  });
183}
184
185/**
186 * Parse accept `str`, returning
187 * an array objects containing
188 * `.type` and `.subtype` along
189 * with the values provided by
190 * `parseQuality()`.
191 *
192 * @param {Type} name
193 * @return {Type}
194 * @api private
195 */
196
197exports.parseAccept = function(str){
198  return exports
199    .parseParams(str)
200    .map(function(obj){
201      var parts = obj.value.split('/');
202      obj.type = parts[0];
203      obj.subtype = parts[1];
204      return obj;
205    });
206};
207
208/**
209 * Parse quality `str`, returning an
210 * array of objects with `.value`,
211 * `.quality` and optional `.params`
212 *
213 * @param {String} str
214 * @return {Array}
215 * @api private
216 */
217
218exports.parseParams = function(str){
219  return str
220    .split(/ *, */)
221    .map(acceptParams)
222    .filter(function(obj){
223      return obj.quality;
224    })
225    .sort(function(a, b){
226      if (a.quality === b.quality) {
227        return a.originalIndex - b.originalIndex;
228      } else {
229        return b.quality - a.quality;
230      }
231    });
232};
233
234/**
235 * Parse accept params `str` returning an
236 * object with `.value`, `.quality` and `.params`.
237 * also includes `.originalIndex` for stable sorting
238 *
239 * @param {String} str
240 * @return {Object}
241 * @api private
242 */
243
244function acceptParams(str, index) {
245  var parts = str.split(/ *; */);
246  var ret = { value: parts[0], quality: 1, params: {}, originalIndex: index };
247
248  for (var i = 1; i < parts.length; ++i) {
249    var pms = parts[i].split(/ *= */);
250    if ('q' == pms[0]) {
251      ret.quality = parseFloat(pms[1]);
252    } else {
253      ret.params[pms[0]] = pms[1];
254    }
255  }
256
257  return ret;
258}
259
260/**
261 * Escape special characters in the given string of html.
262 *
263 * @param  {String} html
264 * @return {String}
265 * @api private
266 */
267
268exports.escape = function(html) {
269  return String(html)
270    .replace(/&/g, '&amp;')
271    .replace(/"/g, '&quot;')
272    .replace(/</g, '&lt;')
273    .replace(/>/g, '&gt;');
274};
275
276/**
277 * Normalize the given path string,
278 * returning a regular expression.
279 *
280 * An empty array should be passed,
281 * which will contain the placeholder
282 * key names. For example "/user/:id" will
283 * then contain ["id"].
284 *
285 * @param  {String|RegExp|Array} path
286 * @param  {Array} keys
287 * @param  {Boolean} sensitive
288 * @param  {Boolean} strict
289 * @return {RegExp}
290 * @api private
291 */
292
293exports.pathRegexp = function(path, keys, sensitive, strict) {
294  if (toString.call(path) == '[object RegExp]') return path;
295  if (Array.isArray(path)) path = '(' + path.join('|') + ')';
296  path = path
297    .concat(strict ? '' : '/?')
298    .replace(/\/\(/g, '(?:/')
299    .replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?(\*)?/g, function(_, slash, format, key, capture, optional, star){
300      keys.push({ name: key, optional: !! optional });
301      slash = slash || '';
302      return ''
303        + (optional ? '' : slash)
304        + '(?:'
305        + (optional ? slash : '')
306        + (format || '') + (capture || (format && '([^/.]+?)' || '([^/]+?)')) + ')'
307        + (optional || '')
308        + (star ? '(/*)?' : '');
309    })
310    .replace(/([\/.])/g, '\\$1')
311    .replace(/\*/g, '(.*)');
312  return new RegExp('^' + path + '$', sensitive ? '' : 'i');
313}
Note: See TracBrowser for help on using the repository browser.