~ubuntu-branches/ubuntu/precise/nodejs/precise

« back to all changes in this revision

Viewing changes to lib/querystring.js

  • Committer: Bazaar Package Importer
  • Author(s): Jérémy Lal
  • Date: 2010-08-20 11:49:04 UTC
  • mfrom: (7.1.6 sid)
  • Revision ID: james.westby@ubuntu.com-20100820114904-lz22w6fkth7yh179
Tags: 0.2.0-1
New upstream release

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
// Query String Utilities
2
2
 
3
3
var QueryString = exports;
 
4
var urlDecode = process.binding("http_parser").urlDecode;
4
5
 
5
 
QueryString.unescape = function (str, decodeSpaces) {
6
 
  return decodeURIComponent(decodeSpaces ? str.replace(/\+/g, " ") : str);
7
 
};
 
6
// a safe fast alternative to decodeURIComponent
 
7
QueryString.unescape = urlDecode;
8
8
 
9
9
QueryString.escape = function (str) {
10
10
  return encodeURIComponent(str);
25
25
 * @param name {String} (optional) Name of the current key, for handling children recursively.
26
26
 * @static
27
27
 */
28
 
QueryString.stringify = function (obj, sep, eq, munge, name) {
29
 
  munge = typeof(munge) == "undefined" ? true : munge;
 
28
QueryString.stringify = QueryString.encode = function (obj, sep, eq, munge, name) {
 
29
  munge = typeof munge == "undefined" || munge;
30
30
  sep = sep || "&";
31
31
  eq = eq || "=";
32
 
  if (isA(obj, null) || isA(obj, undefined) || typeof(obj) === 'function') {
33
 
    return name ? encodeURIComponent(name) + eq : '';
 
32
  var type = Object.prototype.toString.call(obj);
 
33
  if (obj == null || type == "[object Function]" || type == "[object Number]" && !isFinite(obj)) {
 
34
    return name ? QueryString.escape(name) + eq : "";
34
35
  }
35
36
 
36
 
  if (isBool(obj)) obj = +obj;
37
 
  if (isNumber(obj) || isString(obj)) {
38
 
    return encodeURIComponent(name) + eq + encodeURIComponent(obj);
39
 
  }
40
 
  if (isA(obj, [])) {
41
 
    var s = [];
42
 
    name = name+(munge ? '[]' : '');
43
 
    for (var i = 0, l = obj.length; i < l; i ++) {
44
 
      s.push( QueryString.stringify(obj[i], sep, eq, munge, name) );
45
 
    }
46
 
    return s.join(sep);
 
37
  switch (type) {
 
38
    case '[object Boolean]':
 
39
      obj = +obj; // fall through
 
40
    case '[object Number]':
 
41
    case '[object String]':
 
42
      return QueryString.escape(name) + eq + QueryString.escape(obj);
 
43
    case '[object Array]':
 
44
      name = name + (munge ? "[]" : "");
 
45
      return obj.map(function (item) {
 
46
        return QueryString.stringify(item, sep, eq, munge, name);
 
47
      }).join(sep);
47
48
  }
48
49
  // now we know it's an object.
49
50
 
54
55
 
55
56
  stack.push(obj);
56
57
 
57
 
  var s = [];
58
 
  var begin = name ? name + '[' : '';
59
 
  var end = name ? ']' : '';
60
 
  var keys = Object.keys(obj);
61
 
  for (var i = 0, l = keys.length; i < l; i++) {
62
 
    var key = keys[i];
63
 
    var n = begin + key + end;
64
 
    s.push(QueryString.stringify(obj[key], sep, eq, munge, n));
65
 
  }
 
58
  var begin = name ? name + "[" : "",
 
59
      end = name ? "]" : "",
 
60
      keys = Object.keys(obj),
 
61
      n,
 
62
      s = Object.keys(obj).map(function (key) {
 
63
        n = begin + key + end;
 
64
        return QueryString.stringify(obj[key], sep, eq, munge, n);
 
65
      }).join(sep);
66
66
 
67
67
  stack.pop();
68
68
 
69
 
  s = s.join(sep);
70
 
  if (!s && name) return name + "=";
 
69
  if (!s && name) {
 
70
    return name + "=";
 
71
  }
71
72
  return s;
72
73
};
73
74
 
74
 
QueryString.parseQuery = QueryString.parse = function (qs, sep, eq) {
75
 
  return (qs || '')
76
 
    .split(sep||"&")
77
 
    .map(pieceParser(eq||"="))
78
 
    .reduce(mergeParams);
79
 
};
80
 
 
 
75
// matches .xxxxx or [xxxxx] or ['xxxxx'] or ["xxxxx"] with optional [] at the end
 
76
var chunks = /(?:(?:^|\.)([^\[\(\.]+)(?=\[|\.|$|\()|\[([^"'][^\]]*?)\]|\["([^\]"]*?)"\]|\['([^\]']*?)'\])(\[\])?/g;
81
77
// Parse a key=val string.
82
 
// These can get pretty hairy
83
 
// example flow:
84
 
// parse(foo[bar][][bla]=baz)
85
 
// return parse(foo[bar][][bla],"baz")
86
 
// return parse(foo[bar][], {bla : "baz"})
87
 
// return parse(foo[bar], [{bla:"baz"}])
88
 
// return parse(foo, {bar:[{bla:"baz"}]})
89
 
// return {foo:{bar:[{bla:"baz"}]}}
90
 
var trimmerPattern = /^\s+|\s+$/g,
91
 
  slicerPattern = /(.*)\[([^\]]*)\]$/;
92
 
var pieceParser = function (eq) {
93
 
  return function parsePiece (key, val) {
94
 
    if (arguments.length !== 2) {
95
 
      // key=val, called from the map/reduce
96
 
       key = key.split(eq);
97
 
      return parsePiece(
98
 
        QueryString.unescape(key.shift(), true),
99
 
        QueryString.unescape(key.join(eq), true)
100
 
      );
101
 
    }
102
 
    key = key.replace(trimmerPattern, '');
103
 
    if (isString(val)) {
104
 
      val = val.replace(trimmerPattern, '');
105
 
      // convert numerals to numbers
106
 
      if (!isNaN(val)) {
107
 
        var numVal = +val;
108
 
        if (val === numVal.toString(10)) val = numVal;
 
78
QueryString.parse = QueryString.decode = function (qs, sep, eq) {
 
79
  var obj = {};
 
80
  if (qs === undefined) { return {} }
 
81
  String(qs).split(sep || "&").map(function (keyValue) {
 
82
    var res = obj,
 
83
      next,
 
84
      kv = keyValue.split(eq || "="),
 
85
      key = QueryString.unescape(kv.shift(), true),
 
86
      value = QueryString.unescape(kv.join(eq || "="), true);
 
87
    key.replace(chunks, function (all, name, nameInBrackets, nameIn2Quotes, nameIn1Quotes, isArray, offset) {
 
88
      var end = offset + all.length == key.length;
 
89
      name = name || nameInBrackets || nameIn2Quotes || nameIn1Quotes;
 
90
      next = end ? value : {};
 
91
      if (Array.isArray(res[name])) {
 
92
        res[name].push(next);
 
93
        res = next;
 
94
      } else {
 
95
        if (name in res) {
 
96
          if (isArray || end) {
 
97
            res = (res[name] = [res[name], next])[1];
 
98
          } else {
 
99
            res = res[name];
 
100
          }
 
101
        } else {
 
102
          if (isArray) {
 
103
            res = (res[name] = [next])[0];
 
104
          } else {
 
105
            res = res[name] = next;
 
106
          }
 
107
        }
109
108
      }
110
 
    }
111
 
    var sliced = slicerPattern.exec(key);
112
 
    if (!sliced) {
113
 
      var ret = {};
114
 
      if (key) ret[key] = val;
115
 
      return ret;
116
 
    }
117
 
    // ["foo[][bar][][baz]", "foo[][bar][]", "baz"]
118
 
    var tail = sliced[2], head = sliced[1];
119
 
 
120
 
    // array: key[]=val
121
 
    if (!tail) return parsePiece(head, [val]);
122
 
 
123
 
    // obj: key[subkey]=val
124
 
    var ret = {};
125
 
    ret[tail] = val;
126
 
    return parsePiece(head, ret);
127
 
  };
128
 
};
129
 
 
130
 
// the reducer function that merges each query piece together into one set of params
131
 
function mergeParams (params, addition) {
132
 
  return (
133
 
    // if it's uncontested, then just return the addition.
134
 
    (!params) ? addition
135
 
    // if the existing value is an array, then concat it.
136
 
    : (isA(params, [])) ? params.concat(addition)
137
 
    // if the existing value is not an array, and either are not objects, arrayify it.
138
 
    : (!isA(params, {}) || !isA(addition, {})) ? [params].concat(addition)
139
 
    // else merge them as objects, which is a little more complex
140
 
    : mergeObjects(params, addition)
141
 
  );
142
 
};
143
 
 
144
 
// Merge two *objects* together. If this is called, we've already ruled
145
 
// out the simple cases, and need to do a loop.
146
 
function mergeObjects (params, addition) {
147
 
  var keys = Object.keys(addition);
148
 
  for (var i = 0, l = keys.length; i < l; i++) {
149
 
    var key = keys[i];
150
 
    if (key) {
151
 
      params[key] = mergeParams(params[key], addition[key]);
152
 
    }
153
 
  }
154
 
  return params;
155
 
};
156
 
 
157
 
// duck typing
158
 
function isA (thing, canon) {
159
 
  return (
160
 
    // truthiness. you can feel it in your gut.
161
 
    (!thing === !canon)
162
 
    // typeof is usually "object"
163
 
    && typeof(thing) === typeof(canon)
164
 
    // check the constructor
165
 
    && Object.prototype.toString.call(thing) === Object.prototype.toString.call(canon)
166
 
  );
167
 
};
168
 
function isBool (thing) {
169
 
  return (
170
 
    typeof(thing) === "boolean"
171
 
    || isA(thing, new Boolean(thing))
172
 
  );
173
 
};
174
 
function isNumber (thing) {
175
 
  return (
176
 
    typeof(thing) === "number"
177
 
    || isA(thing, new Number(thing))
178
 
  ) && isFinite(thing);
179
 
};
180
 
function isString (thing) {
181
 
  return (
182
 
    typeof(thing) === "string"
183
 
    || isA(thing, new String(thing))
184
 
  );
 
109
    });
 
110
  });
 
111
  return obj;
185
112
};