static htmlUnescape(input) {
    // Used to map HTML entities to characters.
    const htmlUnescapes = {
      '&amp;': '&',
      '&lt;': '<',
      '&gt;': '>',
      '&quot;': '"',
      '&apos;': '\'',
      '&nbsp;': '\u{a0}',
      '&lrm;': '\u{200e}',
      '&rlm;': '\u{200f}',
    };

    // Used to match HTML entities and HTML characters.
    const reEscapedHtml =
        /&(?:amp|lt|gt|quot|apos|nbsp|lrm|rlm|#[xX]?[0-9a-fA-F]+);/g;
    const reHasEscapedHtml = RegExp(reEscapedHtml.source);
    // This check is an optimization, since replace always makes a copy
    if (input && reHasEscapedHtml.test(input)) {
      return input.replace(reEscapedHtml, (entity) => {
        if (entity[1] == '#') {
          // Translate this into an HTML character.
          let code = 0;
          if (entity[2] == 'x' || entity[2] == 'X') {
            // It's hex.
            code = parseInt(entity.substring(3), 16);
          } else {
            // It's decimal.
            code = parseInt(entity.substring(2), 10);
          }
          // Ignore it if it's an invalid code point.
          if (code >= 0 && code <= 0x10FFFF) {
            return String.fromCodePoint(code);
          } else {
            return '';
          }
        }
        // The only thing that might not match the dictionary above is the
        // single quote, which can be matched by many strings in the regex, but
        // only has a single entry in the dictionary.
        return htmlUnescapes[entity] || '\'';
      });
    }
    return input || '';
  }
This method converts the HTML entities &amp;, &lt;, &gt;, &quot;, &#39;,
&nbsp;, &lrm; and &rlm; in string to their corresponding characters.

@param {!string} input
@return {string}