urlencode.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. /**!
  2. * urlencode - lib/urlencode.js
  3. *
  4. * MIT Licensed
  5. *
  6. * Authors:
  7. * fengmk2 <fengmk2@gmail.com> (http://fengmk2.com)
  8. */
  9. "use strict";
  10. /**
  11. * Module dependencies.
  12. */
  13. var iconv = require('iconv-lite');
  14. function isUTF8(charset) {
  15. if (!charset) {
  16. return true;
  17. }
  18. charset = charset.toLowerCase();
  19. return charset === 'utf8' || charset === 'utf-8';
  20. }
  21. function encode(str, charset) {
  22. if (isUTF8(charset)) {
  23. return encodeURIComponent(str);
  24. }
  25. var buf = iconv.encode(str, charset);
  26. var encodeStr = '';
  27. var ch = '';
  28. for (var i = 0; i < buf.length; i++) {
  29. ch = buf[i].toString('16');
  30. if (ch.length === 1) {
  31. ch = '0' + ch;
  32. }
  33. encodeStr += '%' + ch;
  34. }
  35. encodeStr = encodeStr.toUpperCase();
  36. return encodeStr;
  37. }
  38. function decode(str, charset) {
  39. if (isUTF8(charset)) {
  40. return decodeURIComponent(str);
  41. }
  42. var bytes = [];
  43. for (var i = 0; i < str.length; ) {
  44. if (str[i] === '%') {
  45. i++;
  46. bytes.push(parseInt(str.substring(i, i + 2), 16));
  47. i += 2;
  48. } else {
  49. bytes.push(str.charCodeAt(i));
  50. i++;
  51. }
  52. }
  53. var buf = new Buffer(bytes);
  54. return iconv.decode(buf, charset);
  55. }
  56. function parse(qs, sep, eq, options) {
  57. if (typeof sep === 'object') {
  58. // parse(qs, options)
  59. options = sep;
  60. sep = null;
  61. }
  62. sep = sep || '&';
  63. eq = eq || '=';
  64. var obj = {};
  65. if (typeof qs !== 'string' || qs.length === 0) {
  66. return obj;
  67. }
  68. var regexp = /\+/g;
  69. qs = qs.split(sep);
  70. var maxKeys = 1000;
  71. var charset = null;
  72. if (options) {
  73. if (typeof options.maxKeys === 'number') {
  74. maxKeys = options.maxKeys;
  75. }
  76. if (typeof options.charset === 'string') {
  77. charset = options.charset;
  78. }
  79. }
  80. var len = qs.length;
  81. // maxKeys <= 0 means that we should not limit keys count
  82. if (maxKeys > 0 && len > maxKeys) {
  83. len = maxKeys;
  84. }
  85. for (var i = 0; i < len; ++i) {
  86. var x = qs[i].replace(regexp, '%20');
  87. var idx = x.indexOf(eq);
  88. var kstr, vstr, k, v;
  89. if (idx >= 0) {
  90. kstr = x.substr(0, idx);
  91. vstr = x.substr(idx + 1);
  92. } else {
  93. kstr = x;
  94. vstr = '';
  95. }
  96. if (kstr && kstr.indexOf('%') >= 0) {
  97. try {
  98. k = decode(kstr, charset);
  99. } catch (e) {
  100. k = kstr;
  101. }
  102. } else {
  103. k = kstr;
  104. }
  105. if (vstr && vstr.indexOf('%') >= 0) {
  106. try {
  107. v = decode(vstr, charset);
  108. } catch (e) {
  109. v = vstr;
  110. }
  111. } else {
  112. v = vstr;
  113. }
  114. if (!has(obj, k)) {
  115. obj[k] = v;
  116. } else if (Array.isArray(obj[k])) {
  117. obj[k].push(v);
  118. } else {
  119. obj[k] = [obj[k], v];
  120. }
  121. }
  122. return obj;
  123. }
  124. function has(obj, prop) {
  125. return Object.prototype.hasOwnProperty.call(obj, prop);
  126. }
  127. function isASCII(str) {
  128. return (/^[\x00-\x7F]*$/).test(str);
  129. }
  130. function encodeComponent(item, charset) {
  131. item = String(item);
  132. if (isASCII(item)) {
  133. item = encodeURIComponent(item);
  134. } else {
  135. item = encode(item, charset);
  136. }
  137. return item;
  138. }
  139. var stringify = function(obj, prefix, options) {
  140. if (typeof prefix !== 'string') {
  141. options = prefix || {};
  142. prefix = null;
  143. }
  144. var charset = options.charset || 'utf-8';
  145. if (Array.isArray(obj)) {
  146. return stringifyArray(obj, prefix, options);
  147. } else if ('[object Object]' === {}.toString.call(obj)) {
  148. return stringifyObject(obj, prefix, options);
  149. } else if ('string' === typeof obj) {
  150. return stringifyString(obj, prefix, options);
  151. } else {
  152. return prefix + '=' + encodeComponent(String(obj), charset);
  153. }
  154. };
  155. function stringifyString(str, prefix, options) {
  156. if (!prefix) {
  157. throw new TypeError('stringify expects an object');
  158. }
  159. var charset = options.charset;
  160. return prefix + '=' + encodeComponent(str, charset);
  161. }
  162. function stringifyArray(arr, prefix, options) {
  163. var ret = [];
  164. if (!prefix) {
  165. throw new TypeError('stringify expects an object');
  166. }
  167. for (var i = 0; i < arr.length; i++) {
  168. ret.push(stringify(arr[i], prefix + '[' + i + ']', options));
  169. }
  170. return ret.join('&');
  171. }
  172. function stringifyObject(obj, prefix, options) {
  173. var ret = [];
  174. var keys = Object.keys(obj);
  175. var key;
  176. var charset = options.charset;
  177. for (var i = 0, len = keys.length; i < len; ++i) {
  178. key = keys[i];
  179. if ('' === key) {
  180. continue;
  181. }
  182. if (null === obj[key]) {
  183. ret.push(encode(key, charset) + '=');
  184. } else {
  185. ret.push(stringify(
  186. obj[key],
  187. prefix ? prefix + '[' + encodeComponent(key, charset) + ']': encodeComponent(key, charset),
  188. options));
  189. }
  190. }
  191. return ret.join('&');
  192. }
  193. module.exports = encode;
  194. module.exports.encode = encode;
  195. module.exports.decode = decode;
  196. module.exports.parse = parse;
  197. module.exports.stringify = stringify;