Add _.unescape method. [closes #63]

Former-commit-id: 10eada385fd0e1157271a2da6fb32de047d6d88a
This commit is contained in:
John-David Dalton
2012-08-27 02:05:37 -07:00
parent ce440e9f43
commit 8c911a2fd0
3 changed files with 90 additions and 18 deletions

View File

@@ -132,7 +132,6 @@
'each', 'each',
'environment', 'environment',
'escape', 'escape',
'escape',
'evaluate', 'evaluate',
'every', 'every',
'extend', 'extend',
@@ -215,6 +214,7 @@
'throttle', 'throttle',
'times', 'times',
'toArray', 'toArray',
'unescape',
'union', 'union',
'uniq', 'uniq',
'unique', 'unique',

View File

@@ -56,6 +56,9 @@
/** Used to detect delimiter values that should be processed by `tokenizeEvaluate` */ /** Used to detect delimiter values that should be processed by `tokenizeEvaluate` */
var reComplexDelimiter = /[-+=!~*%&^<>|{(\/]|\[\D|\b(?:delete|in|instanceof|new|typeof|void)\b/; var reComplexDelimiter = /[-+=!~*%&^<>|{(\/]|\[\D|\b(?:delete|in|instanceof|new|typeof|void)\b/;
/** Used to match HTML entities */
var reEscapedHtml = /&(?:amp|lt|gt|quot|#[xX]27);/g;
/** Used to match empty string literals in compiled template source */ /** Used to match empty string literals in compiled template source */
var reEmptyStringLeading = /\b__p \+= '';/g, var reEmptyStringLeading = /\b__p \+= '';/g,
reEmptyStringMiddle = /\b(__p \+=) '' \+/g, reEmptyStringMiddle = /\b(__p \+=) '' \+/g,
@@ -74,11 +77,11 @@
.replace(/valueOf|for [^\]]+/g, '.+?') + '$' .replace(/valueOf|for [^\]]+/g, '.+?') + '$'
); );
/** Used to match tokens in template text */ /** Used to match internally used tokens in template text */
var reToken = /__token__(\d+)/g; var reToken = /__token__(\d+)/g;
/** Used to match unescaped characters in strings for inclusion in HTML */ /** Used to match HTML characters */
var reUnescapedHtml = /[&<"']/g; var reUnescapedHtml = /[&<>"']/g;
/** Used to match unescaped characters in compiled string literals */ /** Used to match unescaped characters in compiled string literals */
var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g; var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g;
@@ -233,19 +236,36 @@
cloneableClasses[stringClass] = true; cloneableClasses[stringClass] = true;
/** /**
* Used to escape characters for inclusion in HTML: * Used to convert characters to HTML entities:
* *
* The `>` and `/` characters don't require escaping in HTML and have no * Though the `>` character is escaped for symmetry, characters like `>` and `/`
* special meaning unless they're part of a tag or an unquoted attribute value * don't require escaping in HTML and have no special meaning unless they're part
* http://mathiasbynens.be/notes/ambiguous-ampersands (semi-related fun fact) * of a tag or an unquoted attribute value.
* http://mathiasbynens.be/notes/ambiguous-ampersands (under "semi-related fun fact")
*/ */
var htmlEscapes = { var htmlEscapes = {
'&': '&amp;', '&': '&amp;',
'<': '&lt;', '<': '&lt;',
'>': '&gt;',
'"': '&quot;', '"': '&quot;',
"'": '&#x27;' "'": '&#x27;'
}; };
/**
* Used to convert HTML entities to characters:
*
* Numeric character references are case-insensitive.
* http://www.w3.org/TR/html4/charset.html#h-5.3.1
*/
var htmlUnescapes = {
'&amp;': '&',
'&lt;': '<',
'&gt;': '>',
'&quot;': '"',
'&#x27;': "'",
'&#X27;': "'"
};
/** Used to determine if values are of the language type Object */ /** Used to determine if values are of the language type Object */
var objectTypes = { var objectTypes = {
'boolean': false, 'boolean': false,
@@ -522,7 +542,10 @@
' callback = iteratorBind(callback, thisArg)\n' + ' callback = iteratorBind(callback, thisArg)\n' +
'}', '}',
'inLoop': 'inLoop':
'if (isFunc ? !callback(value, index, object) : indexOf(props, index) < 0) result[index] = value' 'if (isFunc\n' +
' ? !callback(value, index, object)\n' +
' : indexOf(props, index) < 0\n' +
') result[index] = value'
}; };
/** Reusable iterator options for `every` and `some` */ /** Reusable iterator options for `every` and `some` */
@@ -778,7 +801,7 @@
} }
/** /**
* Used by `escape` to escape characters for inclusion in HTML. * Used by `escape` to convert characters to HTML entities.
* *
* @private * @private
* @param {String} match The matched character to escape. * @param {String} match The matched character to escape.
@@ -913,6 +936,17 @@
return token + index; return token + index;
} }
/**
* Used by `unescape` to convert HTML entities to characters.
*
* @private
* @param {String} match The matched character to unescape.
* @returns {String} Returns the unescaped character.
*/
function unescapeHtmlChar(match) {
return htmlUnescapes[match];
}
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
/** /**
@@ -1852,7 +1886,6 @@
* array, string, or `arguments` object. If `value` is an object, size is * array, string, or `arguments` object. If `value` is an object, size is
* determined by returning the number of own enumerable properties it has. * determined by returning the number of own enumerable properties it has.
* *
* @deprecated
* @static * @static
* @memberOf _ * @memberOf _
* @category Objects * @category Objects
@@ -3653,8 +3686,8 @@
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
/** /**
* Escapes a string for inclusion in HTML, replacing `&`, `<`, `"`, and `'` * Converts the characters `&`, `<`, `>`, `"`, and `'` in `string` to their
* characters. * corresponding HTML entities.
* *
* @static * @static
* @memberOf _ * @memberOf _
@@ -4003,6 +4036,24 @@
} }
} }
/**
* Converts the HTML entities `&amp;`, `&lt;`, `&gt;`, `&quot;`, and `&#x27`
* in `string` to their corresponding characters.
*
* @static
* @memberOf _
* @category Utilities
* @param {String} string The string to unescape.
* @returns {String} Returns the unescaped string.
* @example
*
* _.unescape('Moe, Larry &amp; Curly');
* // => "Moe, Larry & Curly"
*/
function unescape(string) {
return string == null ? '' : (string + '').replace(reEscapedHtml, unescapeHtmlChar);
}
/** /**
* Generates a unique id. If `prefix` is passed, the id will be appended to it. * Generates a unique id. If `prefix` is passed, the id will be appended to it.
* *
@@ -4204,6 +4255,7 @@
lodash.throttle = throttle; lodash.throttle = throttle;
lodash.times = times; lodash.times = times;
lodash.toArray = toArray; lodash.toArray = toArray;
lodash.unescape = unescape;
lodash.union = union; lodash.union = union;
lodash.uniq = uniq; lodash.uniq = uniq;
lodash.uniqueId = uniqueId; lodash.uniqueId = uniqueId;

View File

@@ -395,15 +395,11 @@
QUnit.module('lodash.escape'); QUnit.module('lodash.escape');
(function() { (function() {
test('should not escape the ">" character', function() {
equal(_.escape('>'), '>');
});
test('should not escape the "/" character', function() { test('should not escape the "/" character', function() {
equal(_.escape('/'), '/'); equal(_.escape('/'), '/');
}); });
test('should return empty string when passed `null` or `undefined`', function() { test('should return an empty string when passed `null` or `undefined`', function() {
equal(_.escape(null), ''); equal(_.escape(null), '');
equal(_.escape(undefined), ''); equal(_.escape(undefined), '');
}); });
@@ -1540,6 +1536,30 @@
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
QUnit.module('lodash.unescape');
(function() {
test('should perform a case-insensitive replacement of numeric character references', function() {
equal(_.unescape('&#x27;x&#X27;'), "'x'");
});
test('should unescape entities in the correct order', function() {
equal(_.unescape('&amp;lt;'), '&lt;');
});
test('should unescape the proper entities', function() {
var escaped = '&lt;h1&gt;Moe&#x27;s famous &quot;death by chocolate&quot; brownies &amp; cake&lt;\/h1&gt;';
equal(_.unescape(escaped), '<h1>Moe\'s famous "death by chocolate" brownies & cake<\/h1>');
});
test('should return an empty string when passed `null` or `undefined`', function() {
equal(_.unescape(null), '');
equal(_.unescape(undefined), '');
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.uniq'); QUnit.module('lodash.uniq');
(function() { (function() {