const htmlspecialchars_table = { nbsp: "\xA0", quot: '"', apos: "'", lt: "<", gt: ">", amp: "&" }; function htmlspecialchars_decode(s) { // Only a limited set of named entries is supported by design, // because supporting all of them would require a large translation table if (s == null) { // or undefined, because null == undefined return ""; } return s.replace( /&(nbsp|quot|apos|lt|gt|amp|#x[0-9a-f]+|#[0-9]+);/gi, (m, m1) => { if (m1[0] == "#") { return String.fromCharCode( m1[1] == "x" ? parseInt(m1.substr(2), 16) : parseInt(m1.substr(1), 10) ); } return htmlspecialchars_table[m1]; } ); } function parse(content, components, createElement) { content = content.replace(/>\s+<"); const tag_re = /<(\/?)([a-z0-9\-]+)(([^>'"]+|"[^"]*"|'[^']*')*)>/i; const attr_re = /^\s*([^\s="']+)(?:=([^"'\s]+|"[^"]*"|'[^']*'))?/i; let m; let r = []; let stack = [r]; while ((m = tag_re.exec(content))) { let text = content.substr(0, m.index); let close = m[1]; let tag = m[2]; let attrs = m[3]; content = content.substr(m.index + m[0].length); text = text.replace(/^\s+/, "").replace(/\s+$/, ""); if (text !== "") { r.push(htmlspecialchars_decode(text)); } if (close && stack.length > 1) { stack.pop(); r = stack[stack.length - 1]; } else { attrs = attrs.replace(/\s+$/, ""); if (attrs[attrs.length - 1] == "/") { close = true; attrs = attrs.substr(0, attrs.length - 1).replace(/\s+$/, ""); } let attrhash = {}; while ((m = attr_re.exec(attrs))) { let key = m[1].toLowerCase(); let value = m[2]; if (value != null) { // remember that null == undefined, so this checks for undefined too if (value[0] == '"' || value[0] == "'") { value = value.substr(1, value.length - 2); } value = htmlspecialchars_decode(value); } else { value = "true"; } if (key === "href") { // 0x0A-0B-0C are ignored in schema value in IE value = value.replace(/[\x0a\x0b\x0c]+/g, ""); } if (key.substr(0, 2) !== "on" && (key !== "href" || !/^javascript:/.exec(value))) { if (key === "class") { // Convert to className for React key = "className"; } else if (key === "style") { // Convert to an object for React let obj = {}; value = value.replace(/^\s+/, "").replace(/\s+$/, ""); for (let part of value.split(/\s*;\s*/)) { let pos = part.indexOf(":"); if (pos >= 0) { let part_key = part .substr(0, pos) .replace(/\s+$/, "") .replace(/-([a-z])/g, (m, m1) => m1.toUpperCase() ); let part_value = part .substr(pos + 1) .replace(/^\s+/, ""); obj[part_key] = part_value; } } value = obj; } attrhash[key] = value; } attrs = attrs.substr(m[0].length); } if (!close) { attrhash["children"] = []; } attrhash["key"] = r.length; r.push(createElement(components[tag] || tag, attrhash)); if (!close) { stack.push(attrhash["children"]); r = attrhash["children"]; } } } content = content.replace(/^\s+/, "").replace(/\s+$/, ""); if (content !== "") { r.push(htmlspecialchars_decode(content)); } return stack[0]; } module.exports = { htmlspecialchars_decode, parse };