Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/frontend/components/html.tsx
Views: 687
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45// IMPORTANT: we only update this component when the value changes.6// You can't change other things like the style, href_transform function,7// etc. This is an assumption that makes things much more efficient,8// and should be fine for everything in cocalc.910import { CSSProperties as CSS, useEffect, useRef } from "react";11import ReactDOM from "react-dom";12import useIsMountedRef from "@cocalc/frontend/app-framework/is-mounted-hook";13import { is_share_server } from "./share-server";14import { sanitize_html, sanitize_html_safe } from "../misc/sanitize";15import "@cocalc/frontend/misc/process-links/jquery"; // ensure jquery plugin defined.1617declare var $;1819export interface Props {20value?: string;21style?: CSS;22auto_render_math?: boolean; // optional -- used to detect and render math23preProcessMath?: boolean; // if true (the default), and auto_render_math, also run tex2jax.PreProcess to find math in $'s, etc., instead of only rendering <script type="math/tex"...24project_id?: string; // optional -- can be used to improve link handling (e.g., to images)25file_path?: string; // optional -- ...26className?: string; // optional class2728/* optional -- default true, if true scripts and unsafe attributes are removed29from sanitized html WARNING!!! ATTN! false does not work / cannot work!! scripts30will NEVER be run. See commit 1abcd43bd5fff811b5ffaf7c76cb86a0ad494498, which31I've reverted, since it breaks katex... and on balance if we can get by with32other approaches to this problem we should since script is dangerous. See also33https://github.com/sagemathinc/cocalc/issues/469534*/35safeHTML?: boolean;3637// optional function that link/src hrefs are fed through38href_transform?: (string) => string;3940/* optional function post_hook(elt), which should mutate elt, where elt is41the jQuery wrapped set that is created (and discarded!) in the course of42sanitizing input. Use this as an opportunity to modify the HTML structure43before it is exported to text and given to react. Obviously, you can't44install click handlers here.45*/46post_hook?: (any) => void;4748content_editable?: boolean; // if true, makes rendered HTML contenteditable; otherwise, explicitly set false.4950// If true, after any update to component, force reloading of all images.51reload_images?: boolean;5253/* If true, after rendering run the smc_image_scaling pluging to handle54smc-image-scaling= attributes, which are used in smc_sagews to rescale certain55png images produced by other kernels (e.g., the R kernel). See56https://github.com/sagemathinc/cocalc/issues/4421. This functionality is NOT57actually used at all right now, since it doesn't work on the share server58anyways...59*/60smc_image_scaling?: boolean;6162// if true, highlight some <code class='language-r'> </code> blocks.63// this uses a jquery plugin that I wrote that uses codemirror.64highlight_code?: boolean;6566id?: string;6768onClick?: (event?: any) => void;69onDoubleClick?: (event?: any) => void;70}7172export function HTML({73value,74style,75auto_render_math = true,76preProcessMath,77project_id,78file_path,79className,80safeHTML = true,81href_transform,82post_hook,83content_editable,84reload_images,85smc_image_scaling,86highlight_code = true,87id,88onClick,89onDoubleClick,90}: Props) {91const isMountedRef = useIsMountedRef();92const ref = useRef(null);9394function jq(): any {95if (!isMountedRef.current) return;96return $(ReactDOM.findDOMNode(ref.current)) as any;97}9899function update_mathjax(): void {100if (!isMountedRef.current) {101// see https://github.com/sagemathinc/cocalc/issues/1689102return;103}104if (!auto_render_math) {105return;106}107jq()?.katex({ preProcess: preProcessMath ?? true });108}109110function update_links(): void {111if (!isMountedRef.current) {112return;113}114jq()?.process_smc_links({115project_id,116file_path,117href_transform,118});119}120121function update_tables(): void {122if (!isMountedRef.current) {123return;124}125jq()?.find("table").addClass("table");126}127128function update_images(): void {129if (!isMountedRef.current) {130return;131}132if (reload_images) {133jq()?.reload_images();134}135if (smc_image_scaling) {136jq()?.smc_image_scaling();137}138}139140function update_code(): void {141if (isMountedRef.current && highlight_code) {142// note that the highlight_code plugin might not be defined.143jq()?.highlight_code?.();144}145}146147function do_updates(): void {148if (is_share_server()) {149return;150}151update_mathjax();152update_links();153update_tables();154update_code();155update_images();156}157158function update_content(): void {159if (!isMountedRef.current) {160return;161}162do_updates();163}164165useEffect(update_content);166167function render_html(): { __html: string } {168let html;169if (!value) {170return { __html: "" };171}172173if (is_share_server()) {174/* No sanitization at all for share server. For now we175have set things up so that the share server is served176from a different subdomain and user can't sign into it,177so XSS is not an issue. Note that the sanitizing178in the else below (on non-share server) is expensive and179can crash on "big" documents (e.g., 500K).180*/181const elt = $("<div>") as any;182elt.html(value);183if (auto_render_math) {184elt.katex({ preProcess: preProcessMath ?? true });185}186elt.find("table").addClass("table");187if (highlight_code) {188elt.highlight_code();189}190elt.process_smc_links({191project_id,192file_path,193href_transform,194});195html = elt.html();196} else {197if (safeHTML) {198html = sanitize_html_safe(value, post_hook);199} else {200html = sanitize_html(value, true, true, post_hook);201}202}203204return { __html: html };205}206207/* The random key is the whole span (hence the html) does208get rendered whenever this component is updated. Otherwise,209it will NOT re-render except when the value changes.210*/211if (content_editable) {212return (213<div214ref={ref}215id={id}216contentEditable={true}217key={Math.random()}218className={`${className ?? ""} cocalc-html-component`}219dangerouslySetInnerHTML={render_html()}220style={style}221onClick={onClick}222onDoubleClick={onDoubleClick}223></div>224);225} else {226return (227<span228ref={ref}229id={id}230contentEditable={false}231key={Math.random()}232className={`${className ?? ""} cocalc-html-component`}233dangerouslySetInnerHTML={render_html()}234style={style}235onClick={onClick}236onDoubleClick={onDoubleClick}237></span>238);239}240}241242// this displayName is assumed and USED in the packages/hub/share/mathjax-support243// to identify this component; do NOT change or remove!!244HTML.displayName = "Misc-HTML";245246247