224 lines
4.5 KiB
JavaScript
224 lines
4.5 KiB
JavaScript
'use strict';
|
|
var strictUriEncode = require('strict-uri-encode');
|
|
var objectAssign = require('object-assign');
|
|
var decodeComponent = require('decode-uri-component');
|
|
|
|
function encoderForArrayFormat(opts) {
|
|
switch (opts.arrayFormat) {
|
|
case 'index':
|
|
return function (key, value, index) {
|
|
return value === null ? [
|
|
encode(key, opts),
|
|
'[',
|
|
index,
|
|
']'
|
|
].join('') : [
|
|
encode(key, opts),
|
|
'[',
|
|
encode(index, opts),
|
|
']=',
|
|
encode(value, opts)
|
|
].join('');
|
|
};
|
|
|
|
case 'bracket':
|
|
return function (key, value) {
|
|
return value === null ? encode(key, opts) : [
|
|
encode(key, opts),
|
|
'[]=',
|
|
encode(value, opts)
|
|
].join('');
|
|
};
|
|
|
|
default:
|
|
return function (key, value) {
|
|
return value === null ? encode(key, opts) : [
|
|
encode(key, opts),
|
|
'=',
|
|
encode(value, opts)
|
|
].join('');
|
|
};
|
|
}
|
|
}
|
|
|
|
function parserForArrayFormat(opts) {
|
|
var result;
|
|
|
|
switch (opts.arrayFormat) {
|
|
case 'index':
|
|
return function (key, value, accumulator) {
|
|
result = /\[(\d*)\]$/.exec(key);
|
|
|
|
key = key.replace(/\[\d*\]$/, '');
|
|
|
|
if (!result) {
|
|
accumulator[key] = value;
|
|
return;
|
|
}
|
|
|
|
if (accumulator[key] === undefined) {
|
|
accumulator[key] = {};
|
|
}
|
|
|
|
accumulator[key][result[1]] = value;
|
|
};
|
|
|
|
case 'bracket':
|
|
return function (key, value, accumulator) {
|
|
result = /(\[\])$/.exec(key);
|
|
key = key.replace(/\[\]$/, '');
|
|
|
|
if (!result) {
|
|
accumulator[key] = value;
|
|
return;
|
|
} else if (accumulator[key] === undefined) {
|
|
accumulator[key] = [value];
|
|
return;
|
|
}
|
|
|
|
accumulator[key] = [].concat(accumulator[key], value);
|
|
};
|
|
|
|
default:
|
|
return function (key, value, accumulator) {
|
|
if (accumulator[key] === undefined) {
|
|
accumulator[key] = value;
|
|
return;
|
|
}
|
|
|
|
accumulator[key] = [].concat(accumulator[key], value);
|
|
};
|
|
}
|
|
}
|
|
|
|
function encode(value, opts) {
|
|
if (opts.encode) {
|
|
return opts.strict ? strictUriEncode(value) : encodeURIComponent(value);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
function keysSorter(input) {
|
|
if (Array.isArray(input)) {
|
|
return input.sort();
|
|
} else if (typeof input === 'object') {
|
|
return keysSorter(Object.keys(input)).sort(function (a, b) {
|
|
return Number(a) - Number(b);
|
|
}).map(function (key) {
|
|
return input[key];
|
|
});
|
|
}
|
|
|
|
return input;
|
|
}
|
|
|
|
function extract(str) {
|
|
var queryStart = str.indexOf('?');
|
|
if (queryStart === -1) {
|
|
return '';
|
|
}
|
|
return str.slice(queryStart + 1);
|
|
}
|
|
|
|
function parse(str, opts) {
|
|
opts = objectAssign({arrayFormat: 'none'}, opts);
|
|
|
|
var formatter = parserForArrayFormat(opts);
|
|
|
|
// Create an object with no prototype
|
|
// https://github.com/sindresorhus/query-string/issues/47
|
|
var ret = Object.create(null);
|
|
|
|
if (typeof str !== 'string') {
|
|
return ret;
|
|
}
|
|
|
|
str = str.trim().replace(/^[?#&]/, '');
|
|
|
|
if (!str) {
|
|
return ret;
|
|
}
|
|
|
|
str.split('&').forEach(function (param) {
|
|
var parts = param.replace(/\+/g, ' ').split('=');
|
|
// Firefox (pre 40) decodes `%3D` to `=`
|
|
// https://github.com/sindresorhus/query-string/pull/37
|
|
var key = parts.shift();
|
|
var val = parts.length > 0 ? parts.join('=') : undefined;
|
|
|
|
// missing `=` should be `null`:
|
|
// http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters
|
|
val = val === undefined ? null : decodeComponent(val);
|
|
|
|
formatter(decodeComponent(key), val, ret);
|
|
});
|
|
|
|
return Object.keys(ret).sort().reduce(function (result, key) {
|
|
var val = ret[key];
|
|
if (Boolean(val) && typeof val === 'object' && !Array.isArray(val)) {
|
|
// Sort object keys, not values
|
|
result[key] = keysSorter(val);
|
|
} else {
|
|
result[key] = val;
|
|
}
|
|
|
|
return result;
|
|
}, Object.create(null));
|
|
}
|
|
|
|
exports.extract = extract;
|
|
exports.parse = parse;
|
|
|
|
exports.stringify = function (obj, opts) {
|
|
var defaults = {
|
|
encode: true,
|
|
strict: true,
|
|
arrayFormat: 'none'
|
|
};
|
|
|
|
opts = objectAssign(defaults, opts);
|
|
|
|
if (opts.sort === false) {
|
|
opts.sort = function () {};
|
|
}
|
|
|
|
var formatter = encoderForArrayFormat(opts);
|
|
|
|
return obj ? Object.keys(obj).sort(opts.sort).map(function (key) {
|
|
var val = obj[key];
|
|
|
|
if (val === undefined) {
|
|
return '';
|
|
}
|
|
|
|
if (val === null) {
|
|
return encode(key, opts);
|
|
}
|
|
|
|
if (Array.isArray(val)) {
|
|
var result = [];
|
|
|
|
val.slice().forEach(function (val2) {
|
|
if (val2 === undefined) {
|
|
return;
|
|
}
|
|
|
|
result.push(formatter(key, val2, result.length));
|
|
});
|
|
|
|
return result.join('&');
|
|
}
|
|
|
|
return encode(key, opts) + '=' + encode(val, opts);
|
|
}).filter(function (x) {
|
|
return x.length > 0;
|
|
}).join('&') : '';
|
|
};
|
|
|
|
exports.parseUrl = function (str, opts) {
|
|
return {
|
|
url: str.split('?')[0] || '',
|
|
query: parse(extract(str), opts)
|
|
};
|
|
};
|