decode.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. 'use strict';
  2. /* eslint-disable no-bitwise */
  3. var decodeCache = {};
  4. function getDecodeCache(exclude) {
  5. var i, ch, cache = decodeCache[exclude];
  6. if (cache) { return cache; }
  7. cache = decodeCache[exclude] = [];
  8. for (i = 0; i < 128; i++) {
  9. ch = String.fromCharCode(i);
  10. cache.push(ch);
  11. }
  12. for (i = 0; i < exclude.length; i++) {
  13. ch = exclude.charCodeAt(i);
  14. cache[ch] = '%' + ('0' + ch.toString(16).toUpperCase()).slice(-2);
  15. }
  16. return cache;
  17. }
  18. // Decode percent-encoded string.
  19. //
  20. function decode(string, exclude) {
  21. var cache;
  22. if (typeof exclude !== 'string') {
  23. exclude = decode.defaultChars;
  24. }
  25. cache = getDecodeCache(exclude);
  26. return string.replace(/(%[a-f0-9]{2})+/gi, function(seq) {
  27. var i, l, b1, b2, b3, b4, chr,
  28. result = '';
  29. for (i = 0, l = seq.length; i < l; i += 3) {
  30. b1 = parseInt(seq.slice(i + 1, i + 3), 16);
  31. if (b1 < 0x80) {
  32. result += cache[b1];
  33. continue;
  34. }
  35. if ((b1 & 0xE0) === 0xC0 && (i + 3 < l)) {
  36. // 110xxxxx 10xxxxxx
  37. b2 = parseInt(seq.slice(i + 4, i + 6), 16);
  38. if ((b2 & 0xC0) === 0x80) {
  39. chr = ((b1 << 6) & 0x7C0) | (b2 & 0x3F);
  40. if (chr < 0x80) {
  41. result += '\ufffd\ufffd';
  42. } else {
  43. result += String.fromCharCode(chr);
  44. }
  45. i += 3;
  46. continue;
  47. }
  48. }
  49. if ((b1 & 0xF0) === 0xE0 && (i + 6 < l)) {
  50. // 1110xxxx 10xxxxxx 10xxxxxx
  51. b2 = parseInt(seq.slice(i + 4, i + 6), 16);
  52. b3 = parseInt(seq.slice(i + 7, i + 9), 16);
  53. if ((b2 & 0xC0) === 0x80 && (b3 & 0xC0) === 0x80) {
  54. chr = ((b1 << 12) & 0xF000) | ((b2 << 6) & 0xFC0) | (b3 & 0x3F);
  55. if (chr < 0x800 || (chr >= 0xD800 && chr <= 0xDFFF)) {
  56. result += '\ufffd\ufffd\ufffd';
  57. } else {
  58. result += String.fromCharCode(chr);
  59. }
  60. i += 6;
  61. continue;
  62. }
  63. }
  64. if ((b1 & 0xF8) === 0xF0 && (i + 9 < l)) {
  65. // 111110xx 10xxxxxx 10xxxxxx 10xxxxxx
  66. b2 = parseInt(seq.slice(i + 4, i + 6), 16);
  67. b3 = parseInt(seq.slice(i + 7, i + 9), 16);
  68. b4 = parseInt(seq.slice(i + 10, i + 12), 16);
  69. if ((b2 & 0xC0) === 0x80 && (b3 & 0xC0) === 0x80 && (b4 & 0xC0) === 0x80) {
  70. chr = ((b1 << 18) & 0x1C0000) | ((b2 << 12) & 0x3F000) | ((b3 << 6) & 0xFC0) | (b4 & 0x3F);
  71. if (chr < 0x10000 || chr > 0x10FFFF) {
  72. result += '\ufffd\ufffd\ufffd\ufffd';
  73. } else {
  74. chr -= 0x10000;
  75. result += String.fromCharCode(0xD800 + (chr >> 10), 0xDC00 + (chr & 0x3FF));
  76. }
  77. i += 9;
  78. continue;
  79. }
  80. }
  81. result += '\ufffd';
  82. }
  83. return result;
  84. });
  85. }
  86. decode.defaultChars = ';/?:@&=+$,#';
  87. decode.componentChars = '';
  88. module.exports = decode;