1 | 'use strict';
|
2 |
|
3 | var utils = require('./utils');
|
4 | var formats = require('./formats');
|
5 |
|
6 | var arrayPrefixGenerators = {
|
7 | brackets: function brackets(prefix) {
|
8 | return prefix + '[]';
|
9 | },
|
10 | indices: function indices(prefix, key) {
|
11 | return prefix + '[' + key + ']';
|
12 | },
|
13 | repeat: function repeat(prefix) {
|
14 | return prefix;
|
15 | }
|
16 | };
|
17 |
|
18 | var toISO = Date.prototype.toISOString;
|
19 |
|
20 | var defaults = {
|
21 | delimiter: '&',
|
22 | encode: true,
|
23 | encoder: utils.encode,
|
24 | encodeValuesOnly: false,
|
25 | serializeDate: function serializeDate(date) {
|
26 | return toISO.call(date);
|
27 | },
|
28 | skipNulls: false,
|
29 | strictNullHandling: false
|
30 | };
|
31 |
|
32 | var stringify = function stringify( // eslint-disable-line func-name-matching
|
33 | object,
|
34 | prefix,
|
35 | generateArrayPrefix,
|
36 | strictNullHandling,
|
37 | skipNulls,
|
38 | encoder,
|
39 | filter,
|
40 | sort,
|
41 | allowDots,
|
42 | serializeDate,
|
43 | formatter,
|
44 | encodeValuesOnly
|
45 | ) {
|
46 | var obj = object;
|
47 | if (typeof filter === 'function') {
|
48 | obj = filter(prefix, obj);
|
49 | } else if (obj instanceof Date) {
|
50 | obj = serializeDate(obj);
|
51 | } else if (obj === null) {
|
52 | if (strictNullHandling) {
|
53 | return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder) : prefix;
|
54 | }
|
55 |
|
56 | obj = '';
|
57 | }
|
58 |
|
59 | if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean' || utils.isBuffer(obj)) {
|
60 | if (encoder) {
|
61 | var keyValue = encodeValuesOnly ? prefix : encoder(prefix, defaults.encoder);
|
62 | return [formatter(keyValue) + '=' + formatter(encoder(obj, defaults.encoder))];
|
63 | }
|
64 | return [formatter(prefix) + '=' + formatter(String(obj))];
|
65 | }
|
66 |
|
67 | var values = [];
|
68 |
|
69 | if (typeof obj === 'undefined') {
|
70 | return values;
|
71 | }
|
72 |
|
73 | var objKeys;
|
74 | if (Array.isArray(filter)) {
|
75 | objKeys = filter;
|
76 | } else {
|
77 | var keys = Object.keys(obj);
|
78 | objKeys = sort ? keys.sort(sort) : keys;
|
79 | }
|
80 |
|
81 | for (var i = 0; i < objKeys.length; ++i) {
|
82 | var key = objKeys[i];
|
83 |
|
84 | if (skipNulls && obj[key] === null) {
|
85 | continue;
|
86 | }
|
87 |
|
88 | if (Array.isArray(obj)) {
|
89 | values = values.concat(stringify(
|
90 | obj[key],
|
91 | generateArrayPrefix(prefix, key),
|
92 | generateArrayPrefix,
|
93 | strictNullHandling,
|
94 | skipNulls,
|
95 | encoder,
|
96 | filter,
|
97 | sort,
|
98 | allowDots,
|
99 | serializeDate,
|
100 | formatter,
|
101 | encodeValuesOnly
|
102 | ));
|
103 | } else {
|
104 | values = values.concat(stringify(
|
105 | obj[key],
|
106 | prefix + (allowDots ? '.' + key : '[' + key + ']'),
|
107 | generateArrayPrefix,
|
108 | strictNullHandling,
|
109 | skipNulls,
|
110 | encoder,
|
111 | filter,
|
112 | sort,
|
113 | allowDots,
|
114 | serializeDate,
|
115 | formatter,
|
116 | encodeValuesOnly
|
117 | ));
|
118 | }
|
119 | }
|
120 |
|
121 | return values;
|
122 | };
|
123 |
|
124 | module.exports = function (object, opts) {
|
125 | var obj = object;
|
126 | var options = opts ? utils.assign({}, opts) : {};
|
127 |
|
128 | if (options.encoder !== null && options.encoder !== undefined && typeof options.encoder !== 'function') {
|
129 | throw new TypeError('Encoder has to be a function.');
|
130 | }
|
131 |
|
132 | var delimiter = typeof options.delimiter === 'undefined' ? defaults.delimiter : options.delimiter;
|
133 | var strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : defaults.strictNullHandling;
|
134 | var skipNulls = typeof options.skipNulls === 'boolean' ? options.skipNulls : defaults.skipNulls;
|
135 | var encode = typeof options.encode === 'boolean' ? options.encode : defaults.encode;
|
136 | var encoder = typeof options.encoder === 'function' ? options.encoder : defaults.encoder;
|
137 | var sort = typeof options.sort === 'function' ? options.sort : null;
|
138 | var allowDots = typeof options.allowDots === 'undefined' ? false : options.allowDots;
|
139 | var serializeDate = typeof options.serializeDate === 'function' ? options.serializeDate : defaults.serializeDate;
|
140 | var encodeValuesOnly = typeof options.encodeValuesOnly === 'boolean' ? options.encodeValuesOnly : defaults.encodeValuesOnly;
|
141 | if (typeof options.format === 'undefined') {
|
142 | options.format = formats['default'];
|
143 | } else if (!Object.prototype.hasOwnProperty.call(formats.formatters, options.format)) {
|
144 | throw new TypeError('Unknown format option provided.');
|
145 | }
|
146 | var formatter = formats.formatters[options.format];
|
147 | var objKeys;
|
148 | var filter;
|
149 |
|
150 | if (typeof options.filter === 'function') {
|
151 | filter = options.filter;
|
152 | obj = filter('', obj);
|
153 | } else if (Array.isArray(options.filter)) {
|
154 | filter = options.filter;
|
155 | objKeys = filter;
|
156 | }
|
157 |
|
158 | var keys = [];
|
159 |
|
160 | if (typeof obj !== 'object' || obj === null) {
|
161 | return '';
|
162 | }
|
163 |
|
164 | var arrayFormat;
|
165 | if (options.arrayFormat in arrayPrefixGenerators) {
|
166 | arrayFormat = options.arrayFormat;
|
167 | } else if ('indices' in options) {
|
168 | arrayFormat = options.indices ? 'indices' : 'repeat';
|
169 | } else {
|
170 | arrayFormat = 'indices';
|
171 | }
|
172 |
|
173 | var generateArrayPrefix = arrayPrefixGenerators[arrayFormat];
|
174 |
|
175 | if (!objKeys) {
|
176 | objKeys = Object.keys(obj);
|
177 | }
|
178 |
|
179 | if (sort) {
|
180 | objKeys.sort(sort);
|
181 | }
|
182 |
|
183 | for (var i = 0; i < objKeys.length; ++i) {
|
184 | var key = objKeys[i];
|
185 |
|
186 | if (skipNulls && obj[key] === null) {
|
187 | continue;
|
188 | }
|
189 |
|
190 | keys = keys.concat(stringify(
|
191 | obj[key],
|
192 | key,
|
193 | generateArrayPrefix,
|
194 | strictNullHandling,
|
195 | skipNulls,
|
196 | encode ? encoder : null,
|
197 | filter,
|
198 | sort,
|
199 | allowDots,
|
200 | serializeDate,
|
201 | formatter,
|
202 | encodeValuesOnly
|
203 | ));
|
204 | }
|
205 |
|
206 | var joined = keys.join(delimiter);
|
207 | var prefix = options.addQueryPrefix === true ? '?' : '';
|
208 |
|
209 | return joined.length > 0 ? prefix + joined : '';
|
210 | };
|