95 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			95 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict';
 | |
| var token = '%[a-f0-9]{2}';
 | |
| var singleMatcher = new RegExp(token, 'gi');
 | |
| var multiMatcher = new RegExp('(' + token + ')+', 'gi');
 | |
| 
 | |
| function decodeComponents(components, split) {
 | |
| 	try {
 | |
| 		// Try to decode the entire string first
 | |
| 		return decodeURIComponent(components.join(''));
 | |
| 	} catch (err) {
 | |
| 		// Do nothing
 | |
| 	}
 | |
| 
 | |
| 	if (components.length === 1) {
 | |
| 		return components;
 | |
| 	}
 | |
| 
 | |
| 	split = split || 1;
 | |
| 
 | |
| 	// Split the array in 2 parts
 | |
| 	var left = components.slice(0, split);
 | |
| 	var right = components.slice(split);
 | |
| 
 | |
| 	return Array.prototype.concat.call([], decodeComponents(left), decodeComponents(right));
 | |
| }
 | |
| 
 | |
| function decode(input) {
 | |
| 	try {
 | |
| 		return decodeURIComponent(input);
 | |
| 	} catch (err) {
 | |
| 		var tokens = input.match(singleMatcher);
 | |
| 
 | |
| 		for (var i = 1; i < tokens.length; i++) {
 | |
| 			input = decodeComponents(tokens, i).join('');
 | |
| 
 | |
| 			tokens = input.match(singleMatcher);
 | |
| 		}
 | |
| 
 | |
| 		return input;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| function customDecodeURIComponent(input) {
 | |
| 	// Keep track of all the replacements and prefill the map with the `BOM`
 | |
| 	var replaceMap = {
 | |
| 		'%FE%FF': '\uFFFD\uFFFD',
 | |
| 		'%FF%FE': '\uFFFD\uFFFD'
 | |
| 	};
 | |
| 
 | |
| 	var match = multiMatcher.exec(input);
 | |
| 	while (match) {
 | |
| 		try {
 | |
| 			// Decode as big chunks as possible
 | |
| 			replaceMap[match[0]] = decodeURIComponent(match[0]);
 | |
| 		} catch (err) {
 | |
| 			var result = decode(match[0]);
 | |
| 
 | |
| 			if (result !== match[0]) {
 | |
| 				replaceMap[match[0]] = result;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		match = multiMatcher.exec(input);
 | |
| 	}
 | |
| 
 | |
| 	// Add `%C2` at the end of the map to make sure it does not replace the combinator before everything else
 | |
| 	replaceMap['%C2'] = '\uFFFD';
 | |
| 
 | |
| 	var entries = Object.keys(replaceMap);
 | |
| 
 | |
| 	for (var i = 0; i < entries.length; i++) {
 | |
| 		// Replace all decoded components
 | |
| 		var key = entries[i];
 | |
| 		input = input.replace(new RegExp(key, 'g'), replaceMap[key]);
 | |
| 	}
 | |
| 
 | |
| 	return input;
 | |
| }
 | |
| 
 | |
| module.exports = function (encodedURI) {
 | |
| 	if (typeof encodedURI !== 'string') {
 | |
| 		throw new TypeError('Expected `encodedURI` to be of type `string`, got `' + typeof encodedURI + '`');
 | |
| 	}
 | |
| 
 | |
| 	try {
 | |
| 		encodedURI = encodedURI.replace(/\+/g, ' ');
 | |
| 
 | |
| 		// Try the built in decoder first
 | |
| 		return decodeURIComponent(encodedURI);
 | |
| 	} catch (err) {
 | |
| 		// Fallback to a more advanced decoder
 | |
| 		return customDecodeURIComponent(encodedURI);
 | |
| 	}
 | |
| };
 |