106 lines
3.4 KiB
JavaScript
106 lines
3.4 KiB
JavaScript
const ampEntity = { regex: /&(amp|#38|#x26);/g, val: "&" };
|
|
const htmlEntities = {
|
|
"space": { regex: /&(nbsp|#160);/g, val: " " },
|
|
// "lt" : { regex: /&(lt|#60);/g, val: "<" },
|
|
// "gt" : { regex: /&(gt|#62);/g, val: ">" },
|
|
// "amp" : { regex: /&(amp|#38);/g, val: "&" },
|
|
// "quot" : { regex: /&(quot|#34);/g, val: "\"" },
|
|
// "apos" : { regex: /&(apos|#39);/g, val: "'" },
|
|
"cent": { regex: /&(cent|#162);/g, val: "¢" },
|
|
"pound": { regex: /&(pound|#163);/g, val: "£" },
|
|
"yen": { regex: /&(yen|#165);/g, val: "¥" },
|
|
"euro": { regex: /&(euro|#8364);/g, val: "€" },
|
|
"copyright": { regex: /&(copy|#169);/g, val: "©" },
|
|
"reg": { regex: /&(reg|#174);/g, val: "®" },
|
|
"inr": { regex: /&(inr|#8377);/g, val: "₹" },
|
|
"num_dec": { regex: /&#([0-9]{1,7});/g, val: (_, str) => String.fromCodePoint(Number.parseInt(str, 10)) },
|
|
"num_hex": { regex: /&#x([0-9a-fA-F]{1,6});/g, val: (_, str) => String.fromCodePoint(Number.parseInt(str, 16)) },
|
|
};
|
|
|
|
export default class EntitiesParser {
|
|
constructor(replaceHtmlEntities) {
|
|
this.replaceHtmlEntities = replaceHtmlEntities;
|
|
this.docTypeEntities = {};
|
|
this.lastEntities = {
|
|
"apos": { regex: /&(apos|#39|#x27);/g, val: "'" },
|
|
"gt": { regex: /&(gt|#62|#x3E);/g, val: ">" },
|
|
"lt": { regex: /&(lt|#60|#x3C);/g, val: "<" },
|
|
"quot": { regex: /&(quot|#34|#x22);/g, val: "\"" },
|
|
};
|
|
}
|
|
|
|
addExternalEntities(externalEntities) {
|
|
const entKeys = Object.keys(externalEntities);
|
|
for (let i = 0; i < entKeys.length; i++) {
|
|
const ent = entKeys[i];
|
|
this.addExternalEntity(ent, externalEntities[ent])
|
|
}
|
|
}
|
|
addExternalEntity(key, val) {
|
|
validateEntityName(key);
|
|
if (val.indexOf("&") !== -1) {
|
|
reportWarning(`Entity ${key} is not added as '&' is found in value;`)
|
|
return;
|
|
} else {
|
|
this.lastEntities[ent] = {
|
|
regex: new RegExp("&" + key + ";", "g"),
|
|
val: val
|
|
}
|
|
}
|
|
}
|
|
|
|
addDocTypeEntities(entities) {
|
|
const entKeys = Object.keys(entities);
|
|
for (let i = 0; i < entKeys.length; i++) {
|
|
const ent = entKeys[i];
|
|
this.docTypeEntities[ent] = {
|
|
regex: new RegExp("&" + ent + ";", "g"),
|
|
val: entities[ent]
|
|
}
|
|
}
|
|
}
|
|
|
|
parse(val) {
|
|
return this.replaceEntitiesValue(val)
|
|
}
|
|
|
|
/**
|
|
* 1. Replace DOCTYPE entities
|
|
* 2. Replace external entities
|
|
* 3. Replace HTML entities if asked
|
|
* @param {string} val
|
|
*/
|
|
replaceEntitiesValue(val) {
|
|
if (typeof val === "string" && val.length > 0) {
|
|
for (let entityName in this.docTypeEntities) {
|
|
const entity = this.docTypeEntities[entityName];
|
|
val = val.replace(entity.regx, entity.val);
|
|
}
|
|
for (let entityName in this.lastEntities) {
|
|
const entity = this.lastEntities[entityName];
|
|
val = val.replace(entity.regex, entity.val);
|
|
}
|
|
if (this.replaceHtmlEntities) {
|
|
for (let entityName in htmlEntities) {
|
|
const entity = htmlEntities[entityName];
|
|
val = val.replace(entity.regex, entity.val);
|
|
}
|
|
}
|
|
val = val.replace(ampEntity.regex, ampEntity.val);
|
|
}
|
|
return val;
|
|
}
|
|
};
|
|
|
|
//an entity name should not contains special characters that may be used in regex
|
|
//Eg !?\\\/[]$%{}^&*()<>
|
|
const specialChar = "!?\\\/[]$%{}^&*()<>|+";
|
|
|
|
function validateEntityName(name) {
|
|
for (let i = 0; i < specialChar.length; i++) {
|
|
const ch = specialChar[i];
|
|
if (name.indexOf(ch) !== -1) throw new Error(`Invalid character ${ch} in entity name`);
|
|
}
|
|
return name;
|
|
}
|