docs: function htmlParser(html, handler) {
    if (typeof html !== 'string') {
      if (html === null || typeof html === 'undefined') {
        html = '';
      } else {
        html = '' + html;
      }
    }
    var index, chars, match, stack = [],
      last = html,
      text;
    stack.last = function () {
      return stack[stack.length - 1];
    };
  
    while (html) {
      text = '';
      chars = true;
  
      // Make sure we're not in a script or style element
      if (!stack.last() || !specialElements[stack.last()]) {
  
        // Comment
        if (html.indexOf("<!--") === 0) {
          // comments containing -- are not allowed unless they terminate the comment
          index = html.indexOf("--", 4);
  
          if (index >= 0 && html.lastIndexOf("-->", index) === index) {
            if (handler.comment) handler.comment(html.substring(4, index));
            html = html.substring(index + 3);
            chars = false;
          }
          // DOCTYPE
        } else if (DOCTYPE_REGEXP.test(html)) {
          match = html.match(DOCTYPE_REGEXP);
  
          if (match) {
            html = html.replace(match[0], '');
            chars = false;
          }
          // end tag
        } else if (BEGING_END_TAGE_REGEXP.test(html)) {
          match = html.match(END_TAG_REGEXP);
  
          if (match) {
            html = html.substring(match[0].length);
            match[0].replace(END_TAG_REGEXP, parseEndTag);
            chars = false;
          }
  
          // start tag
        } else if (BEGIN_TAG_REGEXP.test(html)) {
          match = html.match(START_TAG_REGEXP);
  
          if (match) {
            // We only have a valid start-tag if there is a '>'.
            if (match[4]) {
              html = html.substring(match[0].length);
              match[0].replace(START_TAG_REGEXP, parseStartTag);
            }
            chars = false;
          } else {
            // no ending tag found --- this piece should be encoded as an entity.
            text += '<';
            html = html.substring(1);
          }
        }
  
        if (chars) {
          index = html.indexOf("<");
  
          text += index < 0 ? html : html.substring(0, index);
          html = index < 0 ? "" : html.substring(index);
  
          if (handler.chars) handler.chars(decodeEntities(text));
        }
  
      } else {
        // IE versions 9 and 10 do not understand the regex '[^]', so using a workaround with [\W\w].
        html = html.replace(new RegExp("([\\W\\w]*)<\\s*\\/\\s*" + stack.last() + "[^>]*>", 'i'),
          function (all, text) {
            text = text.replace(COMMENT_REGEXP, "$1").replace(CDATA_REGEXP, "$1");
  
            if (handler.chars) handler.chars(decodeEntities(text));
  
            return "";
          });
  
        parseEndTag("", stack.last());
      }
  
      if (html == last) {
        // benweet
        // throw $sanitizeMinErr('badparse', "The sanitizer was unable to parse the following block " +
        // 	"of html: {0}", html);
        stack.reverse();
        return stack.cl_each(function (tag) {
          buf.push('</');
          buf.push(tag);
          buf.push('>');
        });
      }
      last = html;
    }
  
    // Clean up any remaining tags
    parseEndTag();
  
    function parseStartTag(tag, tagName, rest, unary) {
      tagName = tagName && tagName.toLowerCase();
      if (blockElements[tagName]) {
        while (stack.last() && inlineElements[stack.last()]) {
          parseEndTag("", stack.last());
        }
      }
  
      if (optionalEndTagElements[tagName] && stack.last() == tagName) {
        parseEndTag("", tagName);
      }
  
      unary = voidElements[tagName] || !!unary;
  
      if (!unary) {
        stack.push(tagName);
      }
  
      var attrs = {};
  
      rest.replace(ATTR_REGEXP,
        function (match, name, doubleQuotedValue, singleQuotedValue, unquotedValue) {
          var value = doubleQuotedValue || singleQuotedValue || unquotedValue || '';
  
          attrs[name] = decodeEntities(value);
        });
      if (handler.start) handler.start(tagName, attrs, unary);
    }
  
    function parseEndTag(tag, tagName) {
      var pos = 0,
        i;
      tagName = tagName && tagName.toLowerCase();
      if (tagName) {
        // Find the closest opened tag of the same type
        for (pos = stack.length - 1; pos >= 0; pos--) {
          if (stack[pos] == tagName) break;
        }
      }
  
      if (pos >= 0) {
        // Close all the open elements, up the stack
        for (i = stack.length - 1; i >= pos; i--)
          if (handler.end) handler.end(stack[i]);
  
        // Remove the open elements from the stack
        stack.length = pos;
      }
    }
  }