@jbrowse/core
Version:
JBrowse 2 core libraries used by plugins
78 lines (77 loc) • 2.07 kB
JavaScript
import { jsx as _jsx } from "react/jsx-runtime";
import { Suspense, lazy, useLayoutEffect, useRef } from 'react';
import escapeHTML from 'escape-html';
import { linkify } from "../util/index.js";
const htmlTags = [
'a',
'b',
'br',
'code',
'div',
'em',
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'i',
'img',
'li',
'mark',
'p',
'pre',
'span',
'small',
'strong',
'table',
'tbody',
'sup',
'sub',
'td',
'tfoot',
'th',
'thead',
'tr',
'u',
'ul',
];
const DOMPurifySanitizedHTML = lazy(() => import("./DOMPurifySanitizedHTML.js"));
const full = new RegExp(htmlTags.map(tag => String.raw `<${tag}\b[^>]*>`).join('|'), 'i');
function isHTML(str) {
return full.test(str);
}
function needsSanitization(str) {
return str.includes('<') || str.includes('://');
}
function SetHTML({ value, className }) {
const spanRef = useRef(null);
useLayoutEffect(() => {
const el = spanRef.current;
if (el) {
try {
el.setHTML(value);
for (const a of el.querySelectorAll('a')) {
a.setAttribute('rel', 'noopener noreferrer');
a.setAttribute('target', '_blank');
}
}
catch (e) {
console.error(e);
}
}
}, [value]);
return _jsx("span", { ref: spanRef, className: className });
}
export default function SanitizedHTML({ html: pre, className, }) {
const str = `${pre}`;
if (!needsSanitization(str)) {
return _jsx("span", { className: className, children: str });
}
const html = linkify(str);
const value = isHTML(html) ? html : escapeHTML(html);
if (typeof Element !== 'undefined' && Element.prototype.setHTML) {
return _jsx(SetHTML, { value: value, className: className });
}
return (_jsx(Suspense, { fallback: _jsx("span", { className: className }), children: _jsx(DOMPurifySanitizedHTML, { value: value, className: className }) }));
}