1 | 'use strict'; |
||
2 | var token = '%[a-f0-9]{2}'; |
||
3 | var singleMatcher = new RegExp(token, 'gi'); |
||
4 | var multiMatcher = new RegExp('(' + token + ')+', 'gi'); |
||
5 | |||
6 | function decodeComponents(components, split) { |
||
7 | try { |
||
8 | // Try to decode the entire string first |
||
9 | return decodeURIComponent(components.join('')); |
||
10 | } catch (err) { |
||
0 ignored issues
–
show
Coding Style
Comprehensibility
Best Practice
introduced
by
![]() |
|||
11 | // Do nothing |
||
12 | } |
||
13 | |||
14 | if (components.length === 1) { |
||
15 | return components; |
||
16 | } |
||
17 | |||
18 | split = split || 1; |
||
19 | |||
20 | // Split the array in 2 parts |
||
21 | var left = components.slice(0, split); |
||
22 | var right = components.slice(split); |
||
23 | |||
24 | return Array.prototype.concat.call([], decodeComponents(left), decodeComponents(right)); |
||
25 | } |
||
26 | |||
27 | function decode(input) { |
||
28 | try { |
||
29 | return decodeURIComponent(input); |
||
30 | } catch (err) { |
||
31 | var tokens = input.match(singleMatcher); |
||
32 | |||
33 | for (var i = 1; i < tokens.length; i++) { |
||
34 | input = decodeComponents(tokens, i).join(''); |
||
35 | |||
36 | tokens = input.match(singleMatcher); |
||
37 | } |
||
38 | |||
39 | return input; |
||
40 | } |
||
41 | } |
||
42 | |||
43 | function customDecodeURIComponent(input) { |
||
44 | // Keep track of all the replacements and prefill the map with the `BOM` |
||
45 | var replaceMap = { |
||
46 | '%FE%FF': '\uFFFD\uFFFD', |
||
47 | '%FF%FE': '\uFFFD\uFFFD' |
||
48 | }; |
||
49 | |||
50 | var match = multiMatcher.exec(input); |
||
51 | while (match) { |
||
52 | try { |
||
53 | // Decode as big chunks as possible |
||
54 | replaceMap[match[0]] = decodeURIComponent(match[0]); |
||
55 | } catch (err) { |
||
56 | var result = decode(match[0]); |
||
57 | |||
58 | if (result !== match[0]) { |
||
59 | replaceMap[match[0]] = result; |
||
60 | } |
||
61 | } |
||
62 | |||
63 | match = multiMatcher.exec(input); |
||
64 | } |
||
65 | |||
66 | // Add `%C2` at the end of the map to make sure it does not replace the combinator before everything else |
||
67 | replaceMap['%C2'] = '\uFFFD'; |
||
68 | |||
69 | var entries = Object.keys(replaceMap); |
||
70 | |||
71 | for (var i = 0; i < entries.length; i++) { |
||
72 | // Replace all decoded components |
||
73 | var key = entries[i]; |
||
74 | input = input.replace(new RegExp(key, 'g'), replaceMap[key]); |
||
75 | } |
||
76 | |||
77 | return input; |
||
78 | } |
||
79 | |||
80 | module.exports = function (encodedURI) { |
||
81 | if (typeof encodedURI !== 'string') { |
||
82 | throw new TypeError('Expected `encodedURI` to be of type `string`, got `' + typeof encodedURI + '`'); |
||
83 | } |
||
84 | |||
85 | try { |
||
86 | encodedURI = encodedURI.replace(/\+/g, ' '); |
||
87 | |||
88 | // Try the built in decoder first |
||
89 | return decodeURIComponent(encodedURI); |
||
90 | } catch (err) { |
||
91 | // Fallback to a more advanced decoder |
||
92 | return customDecodeURIComponent(encodedURI); |
||
93 | } |
||
94 | }; |
||
95 |