228 lines
4.6 KiB
JavaScript
228 lines
4.6 KiB
JavaScript
/**!
|
|
* urlencode - lib/urlencode.js
|
|
*
|
|
* MIT Licensed
|
|
*
|
|
* Authors:
|
|
* fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
/**
|
|
* Module dependencies.
|
|
*/
|
|
|
|
var iconv = require('iconv-lite');
|
|
|
|
function isUTF8(charset) {
|
|
if (!charset) {
|
|
return true;
|
|
}
|
|
charset = charset.toLowerCase();
|
|
return charset === 'utf8' || charset === 'utf-8';
|
|
}
|
|
|
|
function encode(str, charset) {
|
|
if (isUTF8(charset)) {
|
|
return encodeURIComponent(str);
|
|
}
|
|
|
|
var buf = iconv.encode(str, charset);
|
|
var encodeStr = '';
|
|
var ch = '';
|
|
for (var i = 0; i < buf.length; i++) {
|
|
ch = buf[i].toString('16');
|
|
if (ch.length === 1) {
|
|
ch = '0' + ch;
|
|
}
|
|
encodeStr += '%' + ch;
|
|
}
|
|
encodeStr = encodeStr.toUpperCase();
|
|
return encodeStr;
|
|
}
|
|
|
|
function decode(str, charset) {
|
|
if (isUTF8(charset)) {
|
|
return decodeURIComponent(str);
|
|
}
|
|
|
|
var bytes = [];
|
|
for (var i = 0; i < str.length; ) {
|
|
if (str[i] === '%') {
|
|
i++;
|
|
bytes.push(parseInt(str.substring(i, i + 2), 16));
|
|
i += 2;
|
|
} else {
|
|
bytes.push(str.charCodeAt(i));
|
|
i++;
|
|
}
|
|
}
|
|
var buf = new Buffer(bytes);
|
|
return iconv.decode(buf, charset);
|
|
}
|
|
|
|
function parse(qs, sep, eq, options) {
|
|
if (typeof sep === 'object') {
|
|
// parse(qs, options)
|
|
options = sep;
|
|
sep = null;
|
|
}
|
|
|
|
sep = sep || '&';
|
|
eq = eq || '=';
|
|
var obj = {};
|
|
|
|
if (typeof qs !== 'string' || qs.length === 0) {
|
|
return obj;
|
|
}
|
|
|
|
var regexp = /\+/g;
|
|
qs = qs.split(sep);
|
|
|
|
var maxKeys = 1000;
|
|
var charset = null;
|
|
if (options) {
|
|
if (typeof options.maxKeys === 'number') {
|
|
maxKeys = options.maxKeys;
|
|
}
|
|
if (typeof options.charset === 'string') {
|
|
charset = options.charset;
|
|
}
|
|
}
|
|
|
|
var len = qs.length;
|
|
// maxKeys <= 0 means that we should not limit keys count
|
|
if (maxKeys > 0 && len > maxKeys) {
|
|
len = maxKeys;
|
|
}
|
|
|
|
for (var i = 0; i < len; ++i) {
|
|
var x = qs[i].replace(regexp, '%20');
|
|
var idx = x.indexOf(eq);
|
|
var kstr, vstr, k, v;
|
|
|
|
if (idx >= 0) {
|
|
kstr = x.substr(0, idx);
|
|
vstr = x.substr(idx + 1);
|
|
} else {
|
|
kstr = x;
|
|
vstr = '';
|
|
}
|
|
|
|
if (kstr && kstr.indexOf('%') >= 0) {
|
|
try {
|
|
k = decode(kstr, charset);
|
|
} catch (e) {
|
|
k = kstr;
|
|
}
|
|
} else {
|
|
k = kstr;
|
|
}
|
|
|
|
if (vstr && vstr.indexOf('%') >= 0) {
|
|
try {
|
|
v = decode(vstr, charset);
|
|
} catch (e) {
|
|
v = vstr;
|
|
}
|
|
} else {
|
|
v = vstr;
|
|
}
|
|
|
|
if (!has(obj, k)) {
|
|
obj[k] = v;
|
|
} else if (Array.isArray(obj[k])) {
|
|
obj[k].push(v);
|
|
} else {
|
|
obj[k] = [obj[k], v];
|
|
}
|
|
}
|
|
|
|
return obj;
|
|
}
|
|
|
|
function has(obj, prop) {
|
|
return Object.prototype.hasOwnProperty.call(obj, prop);
|
|
}
|
|
|
|
function isASCII(str) {
|
|
return (/^[\x00-\x7F]*$/).test(str);
|
|
}
|
|
|
|
function encodeComponent(item, charset) {
|
|
item = String(item);
|
|
if (isASCII(item)) {
|
|
item = encodeURIComponent(item);
|
|
} else {
|
|
item = encode(item, charset);
|
|
}
|
|
return item;
|
|
}
|
|
|
|
var stringify = function(obj, prefix, options) {
|
|
if (typeof prefix !== 'string') {
|
|
options = prefix || {};
|
|
prefix = null;
|
|
}
|
|
var charset = options.charset || 'utf-8';
|
|
if (Array.isArray(obj)) {
|
|
return stringifyArray(obj, prefix, options);
|
|
} else if ('[object Object]' === {}.toString.call(obj)) {
|
|
return stringifyObject(obj, prefix, options);
|
|
} else if ('string' === typeof obj) {
|
|
return stringifyString(obj, prefix, options);
|
|
} else {
|
|
return prefix + '=' + encodeComponent(String(obj), charset);
|
|
}
|
|
};
|
|
|
|
function stringifyString(str, prefix, options) {
|
|
if (!prefix) {
|
|
throw new TypeError('stringify expects an object');
|
|
}
|
|
var charset = options.charset;
|
|
return prefix + '=' + encodeComponent(str, charset);
|
|
}
|
|
|
|
function stringifyArray(arr, prefix, options) {
|
|
var ret = [];
|
|
if (!prefix) {
|
|
throw new TypeError('stringify expects an object');
|
|
}
|
|
for (var i = 0; i < arr.length; i++) {
|
|
ret.push(stringify(arr[i], prefix + '[' + i + ']', options));
|
|
}
|
|
return ret.join('&');
|
|
}
|
|
|
|
function stringifyObject(obj, prefix, options) {
|
|
var ret = [];
|
|
var keys = Object.keys(obj);
|
|
var key;
|
|
|
|
var charset = options.charset;
|
|
for (var i = 0, len = keys.length; i < len; ++i) {
|
|
key = keys[i];
|
|
if ('' === key) {
|
|
continue;
|
|
}
|
|
if (null === obj[key]) {
|
|
ret.push(encode(key, charset) + '=');
|
|
} else {
|
|
ret.push(stringify(
|
|
obj[key],
|
|
prefix ? prefix + '[' + encodeComponent(key, charset) + ']': encodeComponent(key, charset),
|
|
options));
|
|
}
|
|
}
|
|
|
|
return ret.join('&');
|
|
}
|
|
|
|
module.exports = encode;
|
|
module.exports.encode = encode;
|
|
module.exports.decode = decode;
|
|
module.exports.parse = parse;
|
|
module.exports.stringify = stringify;
|