encode.mjs 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. const encodeCache = {}
  2. // Create a lookup array where anything but characters in `chars` string
  3. // and alphanumeric chars is percent-encoded.
  4. //
  5. function getEncodeCache (exclude) {
  6. let cache = encodeCache[exclude]
  7. if (cache) { return cache }
  8. cache = encodeCache[exclude] = []
  9. for (let i = 0; i < 128; i++) {
  10. const ch = String.fromCharCode(i)
  11. if (/^[0-9a-z]$/i.test(ch)) {
  12. // always allow unencoded alphanumeric characters
  13. cache.push(ch)
  14. } else {
  15. cache.push('%' + ('0' + i.toString(16).toUpperCase()).slice(-2))
  16. }
  17. }
  18. for (let i = 0; i < exclude.length; i++) {
  19. cache[exclude.charCodeAt(i)] = exclude[i]
  20. }
  21. return cache
  22. }
  23. // Encode unsafe characters with percent-encoding, skipping already
  24. // encoded sequences.
  25. //
  26. // - string - string to encode
  27. // - exclude - list of characters to ignore (in addition to a-zA-Z0-9)
  28. // - keepEscaped - don't encode '%' in a correct escape sequence (default: true)
  29. //
  30. function encode (string, exclude, keepEscaped) {
  31. if (typeof exclude !== 'string') {
  32. // encode(string, keepEscaped)
  33. keepEscaped = exclude
  34. exclude = encode.defaultChars
  35. }
  36. if (typeof keepEscaped === 'undefined') {
  37. keepEscaped = true
  38. }
  39. const cache = getEncodeCache(exclude)
  40. let result = ''
  41. for (let i = 0, l = string.length; i < l; i++) {
  42. const code = string.charCodeAt(i)
  43. if (keepEscaped && code === 0x25 /* % */ && i + 2 < l) {
  44. if (/^[0-9a-f]{2}$/i.test(string.slice(i + 1, i + 3))) {
  45. result += string.slice(i, i + 3)
  46. i += 2
  47. continue
  48. }
  49. }
  50. if (code < 128) {
  51. result += cache[code]
  52. continue
  53. }
  54. if (code >= 0xD800 && code <= 0xDFFF) {
  55. if (code >= 0xD800 && code <= 0xDBFF && i + 1 < l) {
  56. const nextCode = string.charCodeAt(i + 1)
  57. if (nextCode >= 0xDC00 && nextCode <= 0xDFFF) {
  58. result += encodeURIComponent(string[i] + string[i + 1])
  59. i++
  60. continue
  61. }
  62. }
  63. result += '%EF%BF%BD'
  64. continue
  65. }
  66. result += encodeURIComponent(string[i])
  67. }
  68. return result
  69. }
  70. encode.defaultChars = ";/?:@&=+$,-_.!~*'()#"
  71. encode.componentChars = "-_.!~*'()"
  72. export default encode