|
1 YUI.add('querystring-parse', function (Y, NAME) { |
|
2 |
|
3 /** |
|
4 * The QueryString module adds support for serializing JavaScript objects into |
|
5 * query strings and parsing JavaScript objects from query strings format. |
|
6 * |
|
7 * The QueryString namespace is added to your YUI instance including static methods |
|
8 * `Y.QueryString.parse(..)` and `Y.QueryString.stringify(..)`. |
|
9 * |
|
10 * The `querystring` module is a alias for `querystring-parse` and |
|
11 * `querystring-stringify`. |
|
12 * |
|
13 * As their names suggest, `querystring-parse` adds support for parsing |
|
14 * Query String data (`Y.QueryString.parse`) and `querystring-stringify` for serializing |
|
15 * JavaScript data into Query Strings (`Y.QueryString.stringify`). You may choose to |
|
16 * include either of the submodules individually if you don't need the |
|
17 * complementary functionality, or include the rollup for both. |
|
18 * |
|
19 * @module querystring |
|
20 * @main querystring |
|
21 */ |
|
22 |
|
23 /** |
|
24 * Provides Y.QueryString.parse method to accept Query Strings and return native |
|
25 * JavaScript objects. |
|
26 * |
|
27 * @module querystring |
|
28 * @submodule querystring-parse |
|
29 */ |
|
30 |
|
31 /** |
|
32 * The QueryString module adds support for serializing JavaScript objects into |
|
33 * query strings and parsing JavaScript objects from query strings format. |
|
34 * @class QueryString |
|
35 * @static |
|
36 */ |
|
37 var QueryString = Y.namespace("QueryString"), |
|
38 |
|
39 // Parse a key=val string. |
|
40 // These can get pretty hairy |
|
41 // example flow: |
|
42 // parse(foo[bar][][bla]=baz) |
|
43 // return parse(foo[bar][][bla],"baz") |
|
44 // return parse(foo[bar][], {bla : "baz"}) |
|
45 // return parse(foo[bar], [{bla:"baz"}]) |
|
46 // return parse(foo, {bar:[{bla:"baz"}]}) |
|
47 // return {foo:{bar:[{bla:"baz"}]}} |
|
48 pieceParser = function (eq) { |
|
49 return function parsePiece (key, val) { |
|
50 |
|
51 var sliced, numVal, head, tail, ret; |
|
52 |
|
53 if (arguments.length !== 2) { |
|
54 // key=val, called from the map/reduce |
|
55 key = key.split(eq); |
|
56 return parsePiece( |
|
57 QueryString.unescape(key.shift()), |
|
58 QueryString.unescape(key.join(eq)) |
|
59 ); |
|
60 } |
|
61 key = key.replace(/^\s+|\s+$/g, ''); |
|
62 if (Y.Lang.isString(val)) { |
|
63 val = val.replace(/^\s+|\s+$/g, ''); |
|
64 // convert numerals to numbers |
|
65 if (!isNaN(val)) { |
|
66 numVal = +val; |
|
67 if (val === numVal.toString(10)) { |
|
68 val = numVal; |
|
69 } |
|
70 } |
|
71 } |
|
72 sliced = /(.*)\[([^\]]*)\]$/.exec(key); |
|
73 if (!sliced) { |
|
74 ret = {}; |
|
75 if (key) { |
|
76 ret[key] = val; |
|
77 } |
|
78 return ret; |
|
79 } |
|
80 // ["foo[][bar][][baz]", "foo[][bar][]", "baz"] |
|
81 tail = sliced[2]; |
|
82 head = sliced[1]; |
|
83 |
|
84 // array: key[]=val |
|
85 if (!tail) { |
|
86 return parsePiece(head, [val]); |
|
87 } |
|
88 |
|
89 // obj: key[subkey]=val |
|
90 ret = {}; |
|
91 ret[tail] = val; |
|
92 return parsePiece(head, ret); |
|
93 }; |
|
94 }, |
|
95 |
|
96 // the reducer function that merges each query piece together into one set of params |
|
97 mergeParams = function(params, addition) { |
|
98 return ( |
|
99 // if it's uncontested, then just return the addition. |
|
100 (!params) ? addition |
|
101 // if the existing value is an array, then concat it. |
|
102 : (Y.Lang.isArray(params)) ? params.concat(addition) |
|
103 // if the existing value is not an array, and either are not objects, arrayify it. |
|
104 : (!Y.Lang.isObject(params) || !Y.Lang.isObject(addition)) ? [params].concat(addition) |
|
105 // else merge them as objects, which is a little more complex |
|
106 : mergeObjects(params, addition) |
|
107 ); |
|
108 }, |
|
109 |
|
110 // Merge two *objects* together. If this is called, we've already ruled |
|
111 // out the simple cases, and need to do the for-in business. |
|
112 mergeObjects = function(params, addition) { |
|
113 for (var i in addition) { |
|
114 if (i && addition.hasOwnProperty(i)) { |
|
115 params[i] = mergeParams(params[i], addition[i]); |
|
116 } |
|
117 } |
|
118 return params; |
|
119 }; |
|
120 |
|
121 /** |
|
122 * Accept Query Strings and return native JavaScript objects. |
|
123 * |
|
124 * @method parse |
|
125 * @param qs {String} Querystring to be parsed into an object. |
|
126 * @param sep {String} (optional) Character that should join param k=v pairs together. Default: "&" |
|
127 * @param eq {String} (optional) Character that should join keys to their values. Default: "=" |
|
128 * @public |
|
129 * @static |
|
130 */ |
|
131 QueryString.parse = function (qs, sep, eq) { |
|
132 // wouldn't Y.Array(qs.split()).map(pieceParser(eq)).reduce(mergeParams) be prettier? |
|
133 return Y.Array.reduce( |
|
134 Y.Array.map( |
|
135 qs.split(sep || "&"), |
|
136 pieceParser(eq || "=") |
|
137 ), |
|
138 {}, |
|
139 mergeParams |
|
140 ); |
|
141 }; |
|
142 |
|
143 /** |
|
144 * Provides Y.QueryString.unescape method to be able to override default decoding |
|
145 * method. This is important in cases where non-standard delimiters are used, if |
|
146 * the delimiters would not normally be handled properly by the builtin |
|
147 * (en|de)codeURIComponent functions. |
|
148 * Default: replace "+" with " ", and then decodeURIComponent behavior. |
|
149 * |
|
150 * @method unescape |
|
151 * @param s {String} String to be decoded. |
|
152 * @public |
|
153 * @static |
|
154 **/ |
|
155 QueryString.unescape = function (s) { |
|
156 return decodeURIComponent(s.replace(/\+/g, ' ')); |
|
157 }; |
|
158 |
|
159 |
|
160 |
|
161 |
|
162 }, '@VERSION@', {"requires": ["yui-base", "array-extras"]}); |