UNPKG

@jbrowse/core

Version:

JBrowse 2 core libraries used by plugins

78 lines (77 loc) 2.07 kB
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 }) })); }