commit inicial
[VSoRC/.git] / node_modules / qs / lib / stringify.js
1 'use strict';
2
3 var utils = require('./utils');
4 var formats = require('./formats');
5 var has = Object.prototype.hasOwnProperty;
6
7 var arrayPrefixGenerators = {
8     brackets: function brackets(prefix) { // eslint-disable-line func-name-matching
9         return prefix + '[]';
10     },
11     comma: 'comma',
12     indices: function indices(prefix, key) { // eslint-disable-line func-name-matching
13         return prefix + '[' + key + ']';
14     },
15     repeat: function repeat(prefix) { // eslint-disable-line func-name-matching
16         return prefix;
17     }
18 };
19
20 var isArray = Array.isArray;
21 var push = Array.prototype.push;
22 var pushToArray = function (arr, valueOrArray) {
23     push.apply(arr, isArray(valueOrArray) ? valueOrArray : [valueOrArray]);
24 };
25
26 var toISO = Date.prototype.toISOString;
27
28 var defaults = {
29     addQueryPrefix: false,
30     allowDots: false,
31     charset: 'utf-8',
32     charsetSentinel: false,
33     delimiter: '&',
34     encode: true,
35     encoder: utils.encode,
36     encodeValuesOnly: false,
37     formatter: formats.formatters[formats['default']],
38     // deprecated
39     indices: false,
40     serializeDate: function serializeDate(date) { // eslint-disable-line func-name-matching
41         return toISO.call(date);
42     },
43     skipNulls: false,
44     strictNullHandling: false
45 };
46
47 var stringify = function stringify( // eslint-disable-line func-name-matching
48     object,
49     prefix,
50     generateArrayPrefix,
51     strictNullHandling,
52     skipNulls,
53     encoder,
54     filter,
55     sort,
56     allowDots,
57     serializeDate,
58     formatter,
59     encodeValuesOnly,
60     charset
61 ) {
62     var obj = object;
63     if (typeof filter === 'function') {
64         obj = filter(prefix, obj);
65     } else if (obj instanceof Date) {
66         obj = serializeDate(obj);
67     } else if (generateArrayPrefix === 'comma' && isArray(obj)) {
68         obj = obj.join(',');
69     }
70
71     if (obj === null) {
72         if (strictNullHandling) {
73             return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder, charset) : prefix;
74         }
75
76         obj = '';
77     }
78
79     if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean' || utils.isBuffer(obj)) {
80         if (encoder) {
81             var keyValue = encodeValuesOnly ? prefix : encoder(prefix, defaults.encoder, charset);
82             return [formatter(keyValue) + '=' + formatter(encoder(obj, defaults.encoder, charset))];
83         }
84         return [formatter(prefix) + '=' + formatter(String(obj))];
85     }
86
87     var values = [];
88
89     if (typeof obj === 'undefined') {
90         return values;
91     }
92
93     var objKeys;
94     if (isArray(filter)) {
95         objKeys = filter;
96     } else {
97         var keys = Object.keys(obj);
98         objKeys = sort ? keys.sort(sort) : keys;
99     }
100
101     for (var i = 0; i < objKeys.length; ++i) {
102         var key = objKeys[i];
103
104         if (skipNulls && obj[key] === null) {
105             continue;
106         }
107
108         if (isArray(obj)) {
109             pushToArray(values, stringify(
110                 obj[key],
111                 typeof generateArrayPrefix === 'function' ? generateArrayPrefix(prefix, key) : prefix,
112                 generateArrayPrefix,
113                 strictNullHandling,
114                 skipNulls,
115                 encoder,
116                 filter,
117                 sort,
118                 allowDots,
119                 serializeDate,
120                 formatter,
121                 encodeValuesOnly,
122                 charset
123             ));
124         } else {
125             pushToArray(values, stringify(
126                 obj[key],
127                 prefix + (allowDots ? '.' + key : '[' + key + ']'),
128                 generateArrayPrefix,
129                 strictNullHandling,
130                 skipNulls,
131                 encoder,
132                 filter,
133                 sort,
134                 allowDots,
135                 serializeDate,
136                 formatter,
137                 encodeValuesOnly,
138                 charset
139             ));
140         }
141     }
142
143     return values;
144 };
145
146 var normalizeStringifyOptions = function normalizeStringifyOptions(opts) {
147     if (!opts) {
148         return defaults;
149     }
150
151     if (opts.encoder !== null && opts.encoder !== undefined && typeof opts.encoder !== 'function') {
152         throw new TypeError('Encoder has to be a function.');
153     }
154
155     var charset = opts.charset || defaults.charset;
156     if (typeof opts.charset !== 'undefined' && opts.charset !== 'utf-8' && opts.charset !== 'iso-8859-1') {
157         throw new TypeError('The charset option must be either utf-8, iso-8859-1, or undefined');
158     }
159
160     var format = formats['default'];
161     if (typeof opts.format !== 'undefined') {
162         if (!has.call(formats.formatters, opts.format)) {
163             throw new TypeError('Unknown format option provided.');
164         }
165         format = opts.format;
166     }
167     var formatter = formats.formatters[format];
168
169     var filter = defaults.filter;
170     if (typeof opts.filter === 'function' || isArray(opts.filter)) {
171         filter = opts.filter;
172     }
173
174     return {
175         addQueryPrefix: typeof opts.addQueryPrefix === 'boolean' ? opts.addQueryPrefix : defaults.addQueryPrefix,
176         allowDots: typeof opts.allowDots === 'undefined' ? defaults.allowDots : !!opts.allowDots,
177         charset: charset,
178         charsetSentinel: typeof opts.charsetSentinel === 'boolean' ? opts.charsetSentinel : defaults.charsetSentinel,
179         delimiter: typeof opts.delimiter === 'undefined' ? defaults.delimiter : opts.delimiter,
180         encode: typeof opts.encode === 'boolean' ? opts.encode : defaults.encode,
181         encoder: typeof opts.encoder === 'function' ? opts.encoder : defaults.encoder,
182         encodeValuesOnly: typeof opts.encodeValuesOnly === 'boolean' ? opts.encodeValuesOnly : defaults.encodeValuesOnly,
183         filter: filter,
184         formatter: formatter,
185         serializeDate: typeof opts.serializeDate === 'function' ? opts.serializeDate : defaults.serializeDate,
186         skipNulls: typeof opts.skipNulls === 'boolean' ? opts.skipNulls : defaults.skipNulls,
187         sort: typeof opts.sort === 'function' ? opts.sort : null,
188         strictNullHandling: typeof opts.strictNullHandling === 'boolean' ? opts.strictNullHandling : defaults.strictNullHandling
189     };
190 };
191
192 module.exports = function (object, opts) {
193     var obj = object;
194     var options = normalizeStringifyOptions(opts);
195
196     var objKeys;
197     var filter;
198
199     if (typeof options.filter === 'function') {
200         filter = options.filter;
201         obj = filter('', obj);
202     } else if (isArray(options.filter)) {
203         filter = options.filter;
204         objKeys = filter;
205     }
206
207     var keys = [];
208
209     if (typeof obj !== 'object' || obj === null) {
210         return '';
211     }
212
213     var arrayFormat;
214     if (opts && opts.arrayFormat in arrayPrefixGenerators) {
215         arrayFormat = opts.arrayFormat;
216     } else if (opts && 'indices' in opts) {
217         arrayFormat = opts.indices ? 'indices' : 'repeat';
218     } else {
219         arrayFormat = 'indices';
220     }
221
222     var generateArrayPrefix = arrayPrefixGenerators[arrayFormat];
223
224     if (!objKeys) {
225         objKeys = Object.keys(obj);
226     }
227
228     if (options.sort) {
229         objKeys.sort(options.sort);
230     }
231
232     for (var i = 0; i < objKeys.length; ++i) {
233         var key = objKeys[i];
234
235         if (options.skipNulls && obj[key] === null) {
236             continue;
237         }
238         pushToArray(keys, stringify(
239             obj[key],
240             key,
241             generateArrayPrefix,
242             options.strictNullHandling,
243             options.skipNulls,
244             options.encode ? options.encoder : null,
245             options.filter,
246             options.sort,
247             options.allowDots,
248             options.serializeDate,
249             options.formatter,
250             options.encodeValuesOnly,
251             options.charset
252         ));
253     }
254
255     var joined = keys.join(options.delimiter);
256     var prefix = options.addQueryPrefix === true ? '?' : '';
257
258     if (options.charsetSentinel) {
259         if (options.charset === 'iso-8859-1') {
260             // encodeURIComponent('&#10003;'), the "numeric entity" representation of a checkmark
261             prefix += 'utf8=%26%2310003%3B&';
262         } else {
263             // encodeURIComponent('✓')
264             prefix += 'utf8=%E2%9C%93&';
265         }
266     }
267
268     return joined.length > 0 ? prefix + joined : '';
269 };