169 lines
5.3 KiB
JavaScript
169 lines
5.3 KiB
JavaScript
import React, { useEffect, useRef, useMemo , useState } from 'react'
|
|
import "../modules/courses/katex.css";
|
|
import marked, { getTocContent, cleanToc, getMathExpressions, resetMathExpressions } from '../common/marked';
|
|
import 'code-prettify';
|
|
import dompurify from 'dompurify';
|
|
import { getEmoji } from '../forge/Main/emoji';
|
|
import { emojiList } from '../common/util/emoji';
|
|
import axios from 'axios';
|
|
import imgError from '../images/img-error.png'
|
|
|
|
import { renderToString } from 'katex'
|
|
|
|
const preRegex = /<pre[^>]*>/g;
|
|
const strRegexSub = /:([a-zA-Z_]+):/g;
|
|
const quoteRegex = /\[[#][0-9]{0,}\]\(\/[A-Za-z0-9]{0,}\/[A-Za-z0-9]{0,}\/issues\/[0-9]{0,}\)/g;
|
|
function _unescape(str) {
|
|
let div = document.createElement('div')
|
|
div.innerHTML = str
|
|
return div.childNodes.length === 0 ? "" : div.childNodes[0].nodeValue
|
|
}
|
|
|
|
|
|
|
|
export default ({
|
|
value = '',
|
|
className,
|
|
style = {},
|
|
url,
|
|
owner=undefined,
|
|
projectsId=undefined
|
|
}) => {
|
|
const [ issues , setIssues ] = useState([]);
|
|
|
|
useEffect(()=>{
|
|
if(owner&&projectsId){
|
|
getIssueList();
|
|
}
|
|
},[owner,projectsId])
|
|
|
|
function getIssueList(){
|
|
let matchQuote = str.match(quoteRegex);
|
|
let array = [];
|
|
if(matchQuote && matchQuote.length>0){
|
|
for(var x=0;x<matchQuote.length;x++){
|
|
let getIndex = matchQuote[x].match(/#(\S*)\]/)[1];
|
|
getIndex && array.push(getIndex);
|
|
}
|
|
axios.get(`/${owner}/${projectsId}/issues/index_to_name`,{params:{
|
|
index:array
|
|
}}).then(result=>{
|
|
if(result){
|
|
setIssues(result.data);
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
let str = String(value);
|
|
const html = useMemo(() => {
|
|
let rs = marked(str);
|
|
const math_expressions = getMathExpressions();
|
|
if (str.match(/\[TOC\]/)) {
|
|
rs = rs.replace("<p>[TOC]</p>", getTocContent())
|
|
cleanToc()
|
|
}
|
|
// 循环匹配所有emoji
|
|
let matchStr = str.match(strRegexSub);
|
|
if(matchStr && matchStr.length>0){
|
|
for(var i=0;i < matchStr.length;i++){
|
|
rs = rs.replace(matchStr[i],getEmoji(matchStr[i]));
|
|
}
|
|
}
|
|
if(rs){
|
|
for(let i = 0;i<emojiList.length;i++){
|
|
if(rs.indexOf(emojiList[i].code)>=0){
|
|
let reg = '/\\'+emojiList[i].code+'/g'
|
|
if (emojiList[i].code === ':+1:') {
|
|
// 匹配+1
|
|
reg = /:\+1:/g
|
|
}
|
|
switch(emojiList[i].index){
|
|
case 1:
|
|
rs = rs.replace(eval(reg), `<img style="width:24px;" src="https://testforum.trustie.net/editormd/emoji/${emojiList[i].name}.png"/>`)
|
|
case 2:
|
|
rs = rs.replace(eval(reg), `<img style="width:24px;" src="https://twemoji.maxcdn.com/36x36/${emojiList[i].name}.png"/>`)
|
|
case 3:
|
|
rs = rs.replace(eval(reg), `<i class="fa fa-emoji fa-${emojiList[i].name}"></i>`)
|
|
case 4:
|
|
rs = rs.replace(eval(reg), `<i class="${emojiList[i].name}"></i>`)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(owner && projectsId && issues && issues.length>0){
|
|
for(var x=0;x<issues.length;x++){
|
|
let item = issues[x];
|
|
let content = item.id ? `<a href="`+`/${owner}/${projectsId}/issues/${item.project_issues_index}`+`">#${item.project_issues_index}:${item.subject}</a>` : `<span>#${item.project_issues_index}(已删除)</span>`;
|
|
rs = rs.split(`<a href="`+`/${owner}/${projectsId}/issues/${item.project_issues_index}`+`">#${item.project_issues_index}</a>`).join(content);
|
|
}
|
|
}
|
|
rs = rs.replace(/(__special_katext_id_\d+__)/g, (_match, capture) => {
|
|
const { type, expression } = math_expressions[capture];
|
|
return renderToString(_unescape(expression) || '', { displayMode: true, throwOnError: false,macros: {
|
|
"\\mbox": "\\text{#1}",
|
|
"\\textrm": "\\text{#1}",
|
|
"\\textnormal": "\\text{#1}",
|
|
},
|
|
trust: false})
|
|
})
|
|
rs = dompurify.sanitize(rs.replace(/▁/g, "▁▁▁"))
|
|
rs = rs.split('<img ').join(`<img onerror="javascript:this.src='${ imgError }';"`) // 替换replaceall
|
|
resetMathExpressions()
|
|
return rs
|
|
}, [str,issues]);
|
|
|
|
// 锚点跳转,链接地址里含#对应的id
|
|
useEffect(()=>{
|
|
if(url && url.hash && html){
|
|
let u = url.hash;
|
|
if(u){
|
|
let id = decodeURIComponent(u.split("#")[1]);
|
|
let ele = document.getElementById(id);
|
|
if(ele){
|
|
window.scrollTo(0, ele.offsetTop);
|
|
}
|
|
}
|
|
}
|
|
},[url,html])
|
|
|
|
const el = useRef();
|
|
function onAncherHandler(e) {
|
|
let target = e.target;
|
|
if (target.tagName.toUpperCase() === 'A') {
|
|
let ancher = target.getAttribute('href');
|
|
if (ancher && ancher.startsWith('#')) {
|
|
e.preventDefault()
|
|
let viewEl = document.getElementById(ancher.replace('#', ''))
|
|
if (viewEl) {
|
|
viewEl.scrollIntoView(true)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
useEffect(() => {
|
|
if (el.current && html) {
|
|
if (html.match(preRegex)) {
|
|
window.PR.prettyPrint()
|
|
}
|
|
}
|
|
if (el.current) {
|
|
el.current.addEventListener('click', onAncherHandler)
|
|
return () => {
|
|
el.current.removeEventListener('click', onAncherHandler)
|
|
resetMathExpressions()
|
|
cleanToc()
|
|
}
|
|
}
|
|
}, [html, el.current, onAncherHandler])
|
|
return (
|
|
<div
|
|
ref={el}
|
|
style={style}
|
|
className={`${className ? className : ''} markdown-body`}
|
|
dangerouslySetInnerHTML={{ __html: html }}
|
|
></div>
|
|
)
|
|
} |