locize
Version:
This package adds the incontext editor to your i18next setup.
104 lines (99 loc) • 3.58 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var i18nextSubliminal = require('i18next-subliminal');
var postMessage = require('./postMessage.js');
var store = require('../store.js');
var DANGEROUS_ATTR_NAMES = /^(on\w+|style)$/i;
var URL_ATTR_NAMES = /^(href|src|action|formaction|xlink:href)$/i;
var DANGEROUS_URL_SCHEMES = /^\s*(javascript|data|vbscript|file)\s*:/i;
function isSafeAttributeWrite(attr, value) {
if (typeof attr !== 'string') return false;
if (DANGEROUS_ATTR_NAMES.test(attr)) return false;
if (URL_ATTR_NAMES.test(attr) && typeof value === 'string' && DANGEROUS_URL_SCHEMES.test(value)) return false;
return true;
}
function sanitizeTranslationHtml(html) {
if (typeof html !== 'string') return html;
if (typeof DOMParser === 'undefined') return html;
try {
var doc = new DOMParser().parseFromString("<body>".concat(html, "</body>"), 'text/html');
var disallowedTags = ['SCRIPT', 'IFRAME', 'OBJECT', 'EMBED', 'LINK', 'META', 'BASE', 'STYLE'];
disallowedTags.forEach(function (tag) {
doc.body.querySelectorAll(tag.toLowerCase()).forEach(function (n) {
return n.remove();
});
});
doc.body.querySelectorAll('*').forEach(function (n) {
var attrs = Array.from(n.attributes);
attrs.forEach(function (a) {
var name = a.name;
var val = a.value;
if (/^on/i.test(name)) {
n.removeAttribute(name);
return;
}
if (URL_ATTR_NAMES.test(name) && DANGEROUS_URL_SCHEMES.test(val)) {
n.removeAttribute(name);
}
});
});
return doc.body.innerHTML;
} catch (e) {
return html;
}
}
function setValueOnNode(meta, value) {
var item = store.store.get(meta.eleUniqueID);
if (!item || !item.keys[meta.textType]) return;
var txtWithHiddenMeta = item.subliminal ? i18nextSubliminal.wrap(value, item.subliminal) : value;
if (meta.textType === 'text') {
item.node.textContent = txtWithHiddenMeta;
} else if (meta.textType.indexOf('attr:') === 0) {
var attr = meta.textType.replace('attr:', '');
if (!isSafeAttributeWrite(attr, txtWithHiddenMeta)) return;
item.node.setAttribute(attr, txtWithHiddenMeta);
} else if (meta.textType === 'html') {
var id = "".concat(meta.textType, "-").concat(meta.children);
if (!item.originalChildNodes) {
var clones = [];
item.node.childNodes.forEach(function (c) {
clones.push(c);
});
item.originalChildNodes = clones;
}
var sanitisedHtml = sanitizeTranslationHtml(txtWithHiddenMeta);
if (item.children[id].length === item.node.childNodes.length) {
item.node.innerHTML = sanitisedHtml;
} else {
var children = item.children[id];
var first = children[0].child;
var dummy = document.createElement('div');
dummy.innerHTML = sanitisedHtml;
var nodes = [];
dummy.childNodes.forEach(function (c) {
nodes.push(c);
});
nodes.forEach(function (c) {
try {
item.node.insertBefore(c, first);
} catch (error) {
item.node.appendChild(c);
}
});
children.forEach(function (replaceable) {
if (item.node.contains(replaceable.child)) {
item.node.removeChild(replaceable.child);
}
});
}
}
}
function handler(payload) {
var meta = payload.meta,
value = payload.value;
if (meta && value !== undefined) {
setValueOnNode(meta, value);
}
}
postMessage.api.addHandler('editKey', handler);
exports.setValueOnNode = setValueOnNode;