amuchina
Version:
A work-in-progress HTML sanitizer that strives for: performance like window.Sanitizer, readiness like DOMPurify, and ability to run in a WebWorker like neither of those.
86 lines (85 loc) • 2.58 kB
JavaScript
/* IMPORT */
import { FUNKY_TAG_NAMES } from './constants.js';
/* MAIN */
const cloneDeep = (value) => {
return JSON.parse(JSON.stringify(value));
};
const isElement = (value) => {
return (value.nodeType === 1);
};
const isElementFunky = (value) => {
return FUNKY_TAG_NAMES.has(value.tagName);
};
const isElementAction = (value) => {
return ('action' in value);
};
const isElementIframe = (value) => {
return (value.tagName === 'IFRAME');
};
const isElementFormAction = (value) => {
return ('formAction' in value);
};
const isElementHyperlink = (value) => {
return ('protocol' in value);
};
const isScriptOrDataUrl = (() => {
const re = /^(?:\w+script|data):/i;
return (url) => {
return re.test(url);
};
})();
const isScriptOrDataUrlLoose = (() => {
const re = /(?:script|data):/i;
return (url) => {
return re.test(url);
};
})();
const mergeMaps = (maps) => {
const merged = {};
for (let i = 0, l = maps.length; i < l; i++) {
const map = maps[i];
for (const key in map) {
if (!merged[key]) {
merged[key] = map[key];
}
else {
merged[key] = merged[key].concat(map[key]);
}
}
}
return merged;
};
const traverseElementsBasic = (parent, callback) => {
let current = parent.firstChild;
while (current) {
const next = current.nextSibling;
if (isElement(current)) {
callback(current, parent);
if (current.parentNode) { // Still connected, so recurse
traverseElementsBasic(current, callback);
}
}
current = next;
}
};
const traverseElementsIterator = (parent, callback) => {
const iterator = document.createNodeIterator(parent, NodeFilter.SHOW_ELEMENT);
let current;
while (current = iterator.nextNode()) {
const parent = current.parentNode;
if (!parent)
continue;
callback(current, parent); //TSC
}
};
const traverseElements = (parent, callback) => {
const hasIterator = !!globalThis.document && !!globalThis.document.createNodeIterator; // For better WebWorker support
if (hasIterator) {
return traverseElementsIterator(parent, callback);
}
else {
return traverseElementsBasic(parent, callback);
}
};
/* EXPORT */
export { cloneDeep, isElement, isElementFunky, isElementAction, isElementIframe, isElementFormAction, isElementHyperlink, isScriptOrDataUrl, isScriptOrDataUrlLoose, mergeMaps, traverseElements };