simple-jsx-parser/jsxParser.js

150 lines
4.8 KiB
JavaScript

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+</g, "><");
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 };