import React, {Component, PureComponent} from 'react'; import {format_time,Time,TitleLine} from './infrastructure/widgets'; import {THUHOLE_API_ROOT} from './flows_api'; import HtmlToReact from 'html-to-react' import './Common.css'; import { URL_PID_RE, URL_RE, PID_RE, NICKNAME_RE, split_text } from './text_splitter'; import renderMd from './Markdown' export {format_time,Time,TitleLine}; export const API_BASE=THUHOLE_API_ROOT+'services/thuhole'; // https://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex function escape_regex(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string } export function build_highlight_re(txt,split,option='g') { return txt ? new RegExp(`(${txt.split(split).filter((x)=>!!x).map(escape_regex).join('|')})`,option) : /^$/g; } export function ColoredSpan(props) { return ( {props.children} ) } function normalize_url(url) { return /^https?:\/\//.test(url) ? url : 'http://'+url; } export class HighlightedText extends PureComponent { render() { return (
                {this.props.parts.map((part,idx)=>{
                    let [rule,p]=part;
                    return (
                        {
                            rule==='url_pid' ? /## :
                            rule==='url' ? {p} :
                            rule==='pid' ? {e.preventDefault(); this.props.show_pid(p.substring(1));}}>{p} :
                            rule==='nickname' ? {p} :
                            rule==='search' ? {p} :
                            p
                        }
                    );
                })}
            
) } } // props: text, show_pid, color_picker export class HighlightedMarkdown extends Component { render() { const props = this.props const processDefs = new HtmlToReact.ProcessNodeDefinitions(React) const processInstructions = [ { shouldProcessNode: (node) => node.name === 'img', // disable images processNode (node) { return (
[图片]
) } }, { shouldProcessNode: (node) => (/^h[123456]$/.test(node.name)), processNode (node, children, index) { let currentLevel = +(node.name[1]) if (currentLevel < 3) currentLevel = 3; const HeadingTag = `h${currentLevel}` return ( {children} ) } }, { shouldProcessNode: (node) => node.name === 'a', processNode (node, children) { return ( {children} ) } }, { shouldProcessNode (node) { return node.type === 'text' // pid, nickname, search }, processNode (node) { const originalText = node.data const splitted = split_text(originalText, [ ['url_pid', URL_PID_RE], ['url',URL_RE], ['pid',PID_RE], ['nickname',NICKNAME_RE], ]) return ( <> {splitted.map(([rule, p], idx) => { return ( { rule==='url_pid' ? /## : rule==='url' ? {p} : rule==='pid' ? {e.preventDefault(); props.show_pid(p.substring(1));}}>{p} : rule==='nickname' ? {p} : rule==='search' ? {p} : p} ) })} ) } }, { shouldProcessNode: () => true, processNode: processDefs.processDefaultNode } ] const parser = new HtmlToReact.Parser() if (props.author && props.text.match(/^(?:#+ |>|```|\t|\s*-|\s*\d+\.)/)) { const renderedMarkdown = renderMd(props.text) return ( <> {props.author} {parser.parseWithInstructions(renderedMarkdown, node => node.type !== 'script', processInstructions)} ) } else { let rawMd = props.text if (props.author) rawMd = props.author + ' ' + rawMd const renderedMarkdown = renderMd(rawMd) return parser.parseWithInstructions(renderedMarkdown, node => node.type !== 'script', processInstructions) } } } window.TEXTAREA_BACKUP={}; export class SafeTextarea extends Component { constructor(props) { super(props); this.state={ text: '', }; this.on_change_bound=this.on_change.bind(this); this.on_keydown_bound=this.on_keydown.bind(this); this.clear=this.clear.bind(this); this.area_ref=React.createRef(); this.change_callback=props.on_change||(()=>{}); this.submit_callback=props.on_submit||(()=>{}); } componentDidMount() { this.setState({ text: window.TEXTAREA_BACKUP[this.props.id]||'' },()=>{ this.change_callback(this.state.text); }); } componentWillUnmount() { window.TEXTAREA_BACKUP[this.props.id]=this.state.text; this.change_callback(this.state.text); } on_change(event) { this.setState({ text: event.target.value, }); this.change_callback(event.target.value); } on_keydown(event) { if(event.key==='Enter' && event.ctrlKey && !event.altKey) { event.preventDefault(); this.submit_callback(); } } clear() { this.setState({ text: '', }); } set(text) { this.change_callback(text); this.setState({ text: text, }); } get() { return this.state.text; } focus() { this.area_ref.current.focus(); } render() { return (